在尝试重构一些最近变得非常慢的代码时,我遇到了一个代码块,它的执行时间超过 5 秒。
代码由 2 条语句组成:
IEnumerable<int> StudentIds = _entities.Filters
.Where(x => x.TeacherId == Profile.TeacherId.Value && x.StudentId != null)
.Select(x => x.StudentId)
.Distinct<int>();
和
_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>();
所以它有点困惑,但首先我从一个表(过滤器)中获得一个不同的 Id 列表,然后我使用它查询另一个表。
这些是相对较小的表,但仍然需要 5 秒以上的查询时间。
我把它放在 LINQPad 中,它显示它首先执行底部查询,然后运行 1000 个“不同”查询。
一时兴起,我通过在末尾添加 .ToArray() 来更改“StudentIds”代码。这将速度提高了 1000 倍……现在完成相同的查询大约需要 100 毫秒。
这是怎么回事?我做错了什么?
最佳答案
这是 Linq 中延迟执行的陷阱之一:在您的第一种方法中,StudentIds
实际上是一个 IQueryable
,而不是内存中的集合。这意味着在第二个查询中使用它会在数据库上再次运行查询 - 每一次。
通过使用 ToArray()
强制执行第一个查询使 StudentIds
成为内存中的集合,并且 Contains
成为第二个查询中的一部分将遍历包含固定项目序列的集合 - 这将映射到等效于 SQL where StudentId in (1,2,3,4)
查询的内容。
这个查询当然会快得多,因为您预先确定了这个序列,而不是每次执行 Where
子句时。您不使用 ToArray()
(我认为)的第二个查询将映射到带有 where exists (...)
子查询的 SQL 查询,该子查询针对每行。
关于c# - Entity Framework + LINQ + "Contains"== 超慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10080732/