Reputation: 9816
I am using asp net core 2.0 and entity framework to access a MySQL database. My model structure looks like
public class Parent
{
[Key]
public int Id { get; set; }
public virtual ICollection<Child> Child { get; set; }
[NotMapped]
public virtual bool HasGrandchild { get; set; }
}
public class Child
{
[Key]
public int Id { get; set; }
public int ParentId { get; set; }
public virtual ICollection<Grandchild> Grandchild { get; set; }
}
public class Grandchild
{
[Key]
public int Id { get; set; }
public int ChildId { get; set; }
}
I want to check whether there are any records of grandchild in the class Parent
. I know I can use Include
and ThenInclude
to retrieve the child and grandchild. However, the Grandchild
is really big. I don't want to return them. Alternatively, I could use for
loop to count the number Grandchild
. For example,
List<Parent> p_list = _context.Parent.ToList();
foreach(Parent p in p_list)
{
List<Child> c_list = _context.Child
.Where(c => c.ParentId == p.Id)
.ToList();
int NumberOfGrandchild = 0;
foreach (Child c in c_list)
{
List<Grandchild> gc_list = _context.Grandchild
.Where(gc =>gc.ChildId == c.Id)
.ToList();
NumberOfGrandchild += gc_list.Count();
}
p.HasGrandchild = false;
if (NumberOfGrandchild > 0) {
p.HasGrandchild = true;
}
}
The performance of this method is very slow, especially for the big table.
Is there any better method to check whether there are any grandchild records.
Upvotes: 0
Views: 474
Reputation: 12815
var parents = _context.Parents
.Where(p => p.Children.SelectMany(c => c.Grandchildren).Any());
This should give you what you're looking for. You want to avoid Include
as much as possible, because it adds the entire table's columns to the query. (At least it did with <= EF5.)
What's happening here is that you're generating subqueries, the lowest of which will get all Children
for the parent
, and see if any Child
has any Grandchild
.
If you want to determine if a Parent
has any Grandhildren
then you can either move that portion of the Where
clause, or just have true
in an anonymous object.
var parents = _context.Parents
.Select(p => new { // Can be other DTO if you have one
Parent = p,
HasGrandchildren = p.Children
.SelectMany(c => c.Grandchildren)
.Any()
});
or
var parents = _context.Parents
.Where(p => p.Children.SelectMany(c => c.Grandchildren).Any())
.Select(p => new {
Parent = p,
HasGrandchildren = true
});
The second one probably be a little more performant because it would be able to perform that within a single query. The first might be slightly slower of a query, but it would also allow you to get everything.
There are also other ways you could do that with a GroupBy
if you want to split them between those that do have grandchildren, and those that don't.
Upvotes: 2