C# 实体框架计数效率
我从数据库中查询学校实体。然后,我尝试通过这样做来获得该学校的学生人数:C# 实体框架计数效率,c#,entity-framework,C#,Entity Framework,我从数据库中查询学校实体。然后,我尝试通过这样做来获得该学校的学生人数: var school = context.schools.Where(s=>s.ID == 1).Single(); int cnt = school.students.Count(); 但我看到发送到数据库的查询得到了该学校的所有学生记录,并且计数是在应用服务器上完成的 而以下内容只是从数据库中查询COUNT(),应该是这样的: int cnt = context.students.Where(s=>s.s
var school = context.schools.Where(s=>s.ID == 1).Single();
int cnt = school.students.Count();
但我看到发送到数据库的查询得到了该学校的所有学生记录,并且计数是在应用服务器上完成的
而以下内容只是从数据库中查询COUNT(),应该是这样的:
int cnt = context.students.Where(s=>s.schoolID == 1).Count();
为什么这两种方法之间存在如此大的差异?第一个查询不应该也使用COUNT()来提高效率吗
注意:不查询学校实体不是一个选项,因为我正在使用它的一些字段。
school.students.Count()
通过LazyLoading工作,因此首先,您在客户端获取学生:school.students
,然后.Count()
。但您可以按以下方式修改代码,即执行一次数据库访问:
var answer = (from sch in context.schools
where sch.ID == 1
join st in context.students on sch.ID equals st.schoolID into subs
from sub in subs.DefaultIfEmpty()
group sub by new { sch.ID, sch.Name, sch.Location } into gr
select new
{
gr.Key.Name,
gr.Key.Location,
Count = gr.Count(x => x != null)
}).First();
sch.Name
,sch.Location
-需要进一步使用的学校字段查询会在最后一刻运行,以尽可能提高效率-称为延迟加载-在第一个示例中,您的Single()
调用会强制数据提供程序比您希望的更慢
当您调用Single()
时,您需要一个完整的学生
记录-所有学生记录列表中的第一个记录,因此必须运行查询(从SchoolId=1的学校获取SchoolId,SchoolName,…)才能获取该数据。然后调用Count()
,除了只选择Count
之外,重放查询
相反,在第二个示例中,仅在调用Where()
之后才调用Count()
Where()
不会强制执行查询,因为您尚未访问任何数据,因此数据提供程序只能在计算COUNT()
后智能地在数据库上播放GET COUNT
-类型的查询
如何判断LINQ方法是否会“播放”您的查询?我记得,如果它返回IQueryable
,它将不会播放您的查询,其他任何内容都会播放。这都与延迟加载有关,但其效果是双重的:
context.schools.Where(s=>s.ID==1).Single()
将返回未加载任何学生的学校实体(惰性加载第1部分)。但是,
school.students.Count()
必须为所有学生点击数据库,因为.students
部分-因为他们没有在第一行加载,所以他们都将被加载(因为您隐式要求-延迟加载部分2),然后Count()
触发
要减少调用,请按照您的建议执行:intcnt=context.students.Where(s=>s.schoolID==1.Count()代码>将导致2个db调用,或者像扩展方法那样创建分组结果。单一接受IEnumerable或IQueryable?@ScottChamberlain如果您是这样要求的,它将返回IQueryable。注意:不查询学校实体不是一个选项,因为我正在使用它的一些字段。
-查询学校并在两个不同的查询中计数您不是有意义的是,如果是计数,那么将var
更改为int,就像var school
中一样,因为您可以看到它不是计数,而是整个行/实体。有时最简单的方法是,包括您要编写的SQL,并将其转换为linq。还有一些人混淆了懒惰加载的实际含义,我建议你仔细阅读,因为我对这里的其他理解有着复杂的感受。