Reputation: 68
I've been struggling to get my head around this for a while so figured I would post up here.
If I was to have a 1->Many relationship between a Parent object and a Child object. Then retrieved my Parent objects from my Context into a List object. I then iterate through said list setting a "virtual" property on each Child record. The "virtual" property is an enumeration that is defined defines a status, ie New, Unchanged, Invalid. Its implemented as a Property on an Abstract Class that the Entities implement.
Once I have done this I want to get a count of all the child objects that have a status of say "New".
Parent.Sum(p => p.Child.Where(c => c.Status == New).Count())
The first problem is that this goes back to the database once for each parent record and gets all linked child records.
Second issue is that because the Status field is not an actual database field, the values are all at the enumerations default value.
Any help with this would be greatly appreciated.
EDIT:
So firstly, I don't want the Status column in the database. I can easily add it if I want to but its not something I want to persist.
This is probably the best sample of full code that I can give.
List<Parent> parents = myDB.Parents.ToList();
foreach(Parent parent in parents)
{
foreach(Child child in parent.Child)
{
if (condition1)
{
child.Status == Statuses.Status1;
}
else if (condition2)
{
child.Status == Statuses.Status2;
}
else if (condition3)
{
child.Status == Statuses.Status3;
}
}
}
Console.WriteLine(Parent.Sum(p => p.Child.Where(c => c.Status == New).Count()))
Upvotes: 4
Views: 9550
Reputation: 2704
We should also look to disable lazy loading. This usually causes that navigation properties to query the database. A quick way to get the child counts into a view model is shown in this article:
http://www.ozkary.com/2015/04/entity-framework-associated-table.html
Upvotes: 0
Reputation: 18977
Something like this may work:
var parents = db.Parents.ToList();
int count = parents.Childs.Where(c => db.Entry(c).State == EntityState.Added);
This approach solves your first problem. Because the .ToList()
creates a List<Parent>
into the memory and all queries on it won't perform any database query.
However I'm not sure that whether it also solves your second problem or not. The nearest EntityState to your need is EntityState.Added
.
And indeed, it depends on your meaning from New
state...
Upvotes: 0
Reputation: 236328
Use eager loading of child entities to avoid multiple database queries:
var parents = db.Parents.Include(p => p.Child);
Also if you can't map enum property to database column (e.g. EF5 with .NET 3.5), then create integer property which will be mapped to your database column, and mark your enum property as not mapped:
[Column("Status")]
public int StatusInt { get; set; }
[NotMapped]
public StatusType Status
{
get { return (StatusType)StatusInt; }
set { StatusInt = (int)value; }
}
If you will use property mapped to database column, then all calculation will occur on server side:
db.Parents.Sum(p => p.Child.Where(c => c.StatusInt == (int)New).Count())
This will be translated into query like:
SELECT SUM([t2].[value]) AS [value]
FROM (
SELECT (
SELECT COUNT(*)
FROM [Child] AS [t1]
WHERE ([t1].[StatusInt] = @p0) AND ([t1].[ParentID] = [t0].[ParentID])
) AS [value]
FROM [Parent] AS [t0]
) AS [t2]
Upvotes: 2