EF Core:如何直接通过查询为Parent类赋值子项统计数,避免循环
问题描述
我定义了两个实体类:Parent(可包含0个或多个Child)和Child,代码如下:
public class Parent() { int ID { get; set; } string Name { get; set; } int NumberOfChildren { get; set; } }
以及
public class Child() { int ID { get; set; } int ParentID { get; set; } string Name { get; set; } }
目前我通过EF查询获取所有Parent记录并填充NumberOfChildren属性,代码如下:
List<Parent> parentList = []; var tempList = await context.Parents .GroupJoin(context.Children, p => p.ID, c => c.ParentID, (p, c) => new { Parent = p, ChildCount = c.Count() }) .ToListAsync(); foreach (var item in tempList) { item.Parent.NumberOfChildren = item.ChildCount; parentList.Add(item.Parent); }
这种循环更新NumberOfChildren的方式看起来效率较低,请问能否在EF查询中直接设置该属性值,无需循环即可直接得到赋值后的List<Parent>集合?
解决方案
当然可以,你可以直接在EF查询的投影阶段完成NumberOfChildren的赋值,省去后续循环操作,以下是两种可行方案:
方案1:直接投影构造Parent对象
在GroupJoin的结果选择器中,直接创建Parent实例并赋值所有属性,包括计算得到的子项数量:
List<Parent> parentList = await context.Parents .GroupJoin(context.Children, p => p.ID, c => c.ParentID, (p, children) => new Parent { ID = p.ID, Name = p.Name, NumberOfChildren = children.Count() }) .ToListAsync();
该方案会让EF在数据库层面完成分组统计,直接将结果映射为已赋值NumberOfChildren的Parent集合,无需额外处理。
方案2:修改现有Parent实体属性(需注意跟踪状态)
如果需要保留EF对实体的跟踪(比如后续要修改保存),可以在查询中直接修改原实体的属性:
List<Parent> parentList = await context.Parents .GroupJoin(context.Children, p => p.ID, c => c.ParentID, (p, children) => new { Parent = p, ChildCount = children.Count() }) .Select(x => { x.Parent.NumberOfChildren = x.ChildCount; return x.Parent; }) .ToListAsync();
此方案会先完成数据库端的分组统计,在结果加载到本地后直接为每个Parent实体赋值NumberOfChildren,最终返回处理好的集合。需要注意:EF会将这些实体标记为已修改状态,若后续调用SaveChangesAsync(),会把NumberOfChildren的更新写入数据库;如果不需要保存该计算属性,可在查询后调用context.Entry(parent).State = EntityState.Unchanged;取消跟踪。
内容的提问来源于stack exchange,提问作者Iain Carlin
相关产品推荐
相关产品推荐

