Group Linq and return as class - linq

I am querying an EF entity MatchHistory:
public partial class MatchHistory
{
public System.Guid ApplicantId { get; set; }
public int BuyerId { get; set; }
public System.DateTime AppliedOn { get; set; }
public int MatchResultId { get; set; }
public System.DateTime ReapplyOn { get; set; }
public virtual MatchBuyer MatchBuyer { get; set; }
}
I currently have this linq statement in my code.
return r.Find()
.Where(x => x.AppliedOn > cutoff && x.MatchResultId == (int)MatchResult.Accepted)
.ToList();
This will return all rows of the type MatchHistory matching the criteria.
However, what I want to do is group by BuyerId and return a count by BuyerId.
Here's the class, I want to output to:
public class QuotaCount
{
public int BuyerId { get; set; }
public int Count { get; set; }
}
Haven't quite managed to get the right syntax together yet - any advice appreciated.

return r.Find()
.Where(x => x.AppliedOn > cutoff && x.MatchResultId == (int)MatchResult.Accepted)
.GroupBy(x => x.BuyerId)
.Select(x => new QuotaCount { BuyerId = x.Key, Count = x.Count() })
.ToList();

return r.Find()
.Where(x => x.AppliedOn > cutoff && x.MatchResultId == (int)MatchResult.Accepted)
.GroupBy(mh=>mh.BuyerId).Select(gr=>new QuotaCount{BuyerId=gr.Key,Count=gr.Count});

Related

Ignore Soft-Deleted records within graph

Using EF Core
Assuming Structure:
class Parent
{
public int Id { get; set; }
public List<Child> Children { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
}
class Child
{
public int Id { get; set; }
List<GrandChild> Grandchildren { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
public int ParentId { get; set; }
}
class Grandchild
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
public int ChildId { get; set; }
}
Records could have been soft-deleted at any level, although no descendant of a soft-deleted entity can itself not be soft-deleted (obviously).
List<Parent> nonDeletedParents = Context.Parents
.Include(p => p.Children).ThenInclude(c => c.Grandchildren)
.Where(p => !p.IsDeleted)
.ToList();
will get me non-deleted parents, but how do I exclude deleted children and grandchildren in a single query to the database?
One approach I have explored is to use an anonymous return type and then rebuild:
var result = await Context.Parents
.Where(p => !p.IsDeleted)
.Select(p =>
{
Parent = p,
AllChildren = p.Children
.Where(c => !c.IsDeleted)
.ToList(),
AllGrandchildren = p.Children
.Where(c => !c.IsDeleted)
.SelectMany(c => c.Grandchildren)
.Where(g => !g.IsDeleted)
.ToList()
}).ToList();
List<Parent> actualResults = new List<Parent>();
results.ForEach(r =>
{
Parent actualResult = r.Parent;
actualResult.Children = r.AllChildren;
actualResult.Children.ForEach(c =>
{
c.Grandchildren = r.AllGrandchildren.Where(g => g.ChildId == c.Id).ToList();
});
actualResults.Add(actualResult);
}
return actualResults;
But, seems pretty inelegant. Is there a preferable approach?

Fluent LINQ EF Core - Select filtered child property

I have the classes below:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class ParentEntity
{
public Guid Id { get; set; }
public string SomeProperty { get; set; }
public ICollection<ChildEntity> ChildEntities { get; set; }
}
public class ChildEntity
{
public Guid Id { get; set; }
public int Vote { get; set; }
public Guid UserId { get; set; }
}
public class ReturnedParentDto
{
public Guid Id { get; set; }
public string SomeProperty { get; set; }
public int Vote { get; set; }
}
I want to be able to return a full list of ParenEntities, but take an Id of the User class (UserClassId), then filter the ParentEntity's ICollection where UserUid = UserClassId, so only 1 ChildEntity is always returned. Then I would want to extract a specific field from that returned ChildEntity and merge it with the ParentEntity fields. The end result should be like the ReturnedParentDto.
I want to do it in the style like
ParentEntities.Include(v => v.ChildEntities).ToList()
That seems to be possible in EF Core 5, but my project is in 3.1.
You can do this as below
Approach 1:
var result = result = parentEntities.Include(x => x.ChildEntities.Where(y => y.UserId == userId))
.Select(x => new ReturnedParentDto {
Id = x.Id,
SomeProperty = x.SomeProperty,
Vote = x.ChildEntities.FirstOrDefault()?.Vote // userId is the variable here
});
Approach 2:
var result = parentEntities.Select(x =>
new ReturnedParentDto {
Id = x.Id,
SomeProperty = x.SomeProperty,
Vote = x.ChildEntities.FirstOrDefault(y => y.UserId == userId)?.Vote // userId is the variable here
});

How to select 2 objects from 2 tables in one query?

I want to get the below result in one query by linq methods such as .Where, .Select etc., is it possible?
Inner join is the better.
new {
Blog = MyDbContext.Blogs.SingleOrDefault(b => b.Url == "xxx.com"),
Post = MyDbContext.Posts.SingleOrDefault(p => p.Blog.Url == "xxx.com" &&
p.Author == "Jack")
}
Models:
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Author { get; set; }
public Blog Blog { get; set; }
}
public class MyDbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
var ans = MyDbContext.Blogs.Where(b => b.Url == "xxx.com")
.Select(b => new {
Blog = b,
Post = b.Posts.Where(p => p.Author == "Jack").FirstOrDefault()
})
.SingleOrDefault();

How i can LINQ select where? with Filters if Null Values

How i can do LINQ Querie.
List<ApplicationUser> ListaUsuarios = AccountControl.UserManager.Users.Where(i => i.Nombre == Filter.Nombre).ToList();
With many Filters when some attributs can coming with Null Value.
For example:
My FilterGeneric Class have many Attribute accept Nulls.
public class FilterGeneric
{
public DataCollectionType.FiltrosPdf Tipo { get; set; }
public string PdfTitle { get; set; }
public string pdfDescription { get; set; }
public string Nombre { get; set; }
public string Cargo { get; set; }
public string Iniciales { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public bool? Enabled { get; set; }
public DateTime? Date_since { get; set; }
public DateTime? Date_to { get; set; }
public string RoleName { get; set; }
public string Id_Sucursal { get; set; }
public string RUC { get; set; }
public string Direccion { get; set; }
public int? Direccion_Nro { get; set; }
public string Telefono { get; set; }
public int? Id_Localidad { get; set; }
}
Is that Possible? Thanks all for listening.
UPDATE:
I test with Answers
1#:
if (Filter.Nombre != null)
{
query = query.Where(i => i.Nombre == Filter.Nombre);
}
2#:
List<ApplicationUser> ListaUsuarios = AccountControl.UserManager.Users.Where
(x =>
(x.Nombre == Filter.Nombre || string.IsNullOrEmpty(Filter.Nombre)) &&
(x.Nombre == Filter.Cargo || string.IsNullOrEmpty(Filter.Cargo)) &&
(x.Nombre == Filter.Iniciales || string.IsNullOrEmpty(Filter.Iniciales)) &&
(x.Nombre == Filter.UserName || string.IsNullOrEmpty(Filter.UserName))
).ToList();
I get this Error:
You need to add all the filters one by one in the following manner:
List<ApplicationUser> ListaUsuarios =
AccountControl.UserManager.Users
.Where(
i =>
(i.Nombre == Filter.Nombre || string.IsNullOrEmpty(Filter.Nombre))
&&
(i.Cargo == Filter.Cargo || string.IsNullOrEmpty(Filter.Cargo))
).ToList();
This one is telling you, that if Filter.Nombre is null/empty just ignore it. Same case for Filter.Cargo and so on.
For nullable int
(Filter.Direccion_Nro == null || i.Direccion_Nro == Filter.Direccion_Nro.Value)
As the query gets materialized (means executed) when you call something that materializes it (.ToList(), ToArray() or a foreach for example), you can just chain them conditionally:
IEnumerable<ApplicationUser> query = AccountControl.UserManager.Users;
if(Filter.Nombre != null)
{
query = query.Where(i => i.Nombre == Filter.Nombre);
}
List<ApplicationUser> ListaUsuarios = query.ToList();
I think, you can perform it by using Reflection dynamically;
//Determine the not null properties of Filter object
var notNullProperties = Filter.GetType().GetProperties().Where(x => x.GetValue(Filter) != null).ToList();
//Perform where clause for not null properties of Filter
if (notNullProperties.Count > 0)
{
var ListaUsuarios = AccountControl.UserManager.Users.Where(x =>
notNullProperties.All(n => n.GetValue(Filter) == x.GetType().GetProperty(n.Name).GetValue(x))).ToList();
}

LINQ converting anaonymous list to strongly type with date splits in groupBY

I am trying to group together daily logs into months in a linq query from a dbset, getting above or below a percentage of allowed usage.
The dbset is on
public partial class DailyUsageForYear
{
public System.DateTime DateAdded { get; set; }
public Nullable<long> NumUsers { get; set; }
public int AllowedUsageCount { get; set; }
public string Application { get; set; }
public System.Guid ApplicationID { get; set; }
public System.Guid SupplierID { get; set; }
}
I am trying the to get a list of objects like
public class UsageDisplay
{
public int Year { get; set; }
public int Month { get; set; }
public string Application { get; set; }
public int NumUsers { get; set; }
public int AllowedUsageCount { get; set; }
public Guid ApplicationID { get; set; }
}
However the select at the end of my statement does not seem to understand the field names, can anyone tell me please, what am I missing about the linq syntax?
var predicate = PredicateBuilder.False<DailyUsageForYear>();
predicate = predicate.And (x => x.NumUsers > (x.AllowedUsageCount * (DropPercentage/100)));
if (supplier != null)
{
Guid supplierGuid = new Guid (supplier.ToString());
predicate = predicate.And (x => x.SupplierID == supplierGuid);
}
if (Direction == 0)
predicate = predicate.And (x => x.NumUsers < (x.AllowedUsageCount * (Percentage/100)));
else
predicate = predicate.And (x => x.NumUsers > (x.AllowedUsageCount * (Percentage/100)));
usageDetailModel.usage = db.DailyUsageForYears.**Where**(predicate)
.**GroupBy**(x => new { x.DateAdded.Year, x.DateAdded.Month, x.Application, x.NumUsers, x.SeatCount, x.LicenceID })
.**Select**(u => new UsageDisplay() { Year = u.DateAdded.Year, Month = u.DateAdded.Month, Application = u.Application, NumUsers = u.NumUsers, SeatCount = u.SeatCount, ApplicationId = u.ApplicationID });
Inside last select u is not DailyUsageForYear its groupping object with Key field, so try change your select like this
.Select(u => new UsageDisplay() {
Year = u.Key.Year,
Month = u.Key.Month,
Application = u.Key.Application,
NumUsers = u.Key.NumUsers,
SeatCount = u.Key.SeatCount,
ApplicationId = u.Key.ApplicationID
});
also in
.GroupBy(x => new {
x.DateAdded.Year,
x.DateAdded.Month,
x.Application,
x.NumUsers,
x.SeatCount,
x.LicenceID })
i think you need x.ApplicationId instead of x.LicenceID

Resources