Ale*_*scu 16 c# linq entity-framework
试图重构一些最近变得非常慢的代码,我遇到了一个代码块,执行时间超过5秒.
代码由2个语句组成:
IEnumerable<int> StudentIds = _entities.Filters
.Where(x => x.TeacherId == Profile.TeacherId.Value && x.StudentId != null)
.Select(x => x.StudentId)
.Distinct<int>();
Run Code Online (Sandbox Code Playgroud)
和
_entities.StudentClassrooms
.Include("ClassroomTerm.Classroom.School.District")
.Include("ClassroomTerm.Teacher.Profile")
.Include("Student")
.Where(x => StudentIds.Contains(x.StudentId)
&& x.ClassroomTerm.IsActive
&& x.ClassroomTerm.Classroom.IsActive
&& x.ClassroomTerm.Classroom.School.IsActive
&& x.ClassroomTerm.Classroom.School.District.IsActive).AsQueryable<StudentClassroom>();
Run Code Online (Sandbox Code Playgroud)
所以它有点乱,但首先我从一个表(过滤器)得到一个不同的Id列表,然后我使用它查询另一个表.
这些是相对较小的表,但它仍然是5秒以上的查询时间.
我把它放在LINQPad中,它表明它首先执行底部查询,然后运行1000个"不同"查询.
一时兴起,我通过在最后添加.ToArray()来更改"StudentIds"代码.这提高了速度1000x ...现在需要100ms才能完成相同的查询.
这是怎么回事?我究竟做错了什么?
Bro*_*ass 24
这是Linq中延迟执行的陷阱之一:第一种方法StudentIds实际上是IQueryable一种内存集合,而不是内存中的集合.这意味着在第二个查询中使用它将在数据库上再次运行查询 - 每次都是这样.
通过使用强制第一个查询的执行ToArray()使得StudentIds在内存中的集合和Contains在你的第二个查询将运行在此集合,其中包含项目的固定序列部分-这被映射到的东西相当于一个SQL where StudentId in (1,2,3,4)查询.
当然,这个查询要快得多,因为你预先确定了这个序列,而不是每次Where执行该子句.您不使用ToArray()(我认为)的第二个查询将映射到SQL查询,其中包含where exists (...)针对每一行计算的子查询.