c# - Entity Framework + LINQ + "Contains"== 超慢?

标签 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>();

_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/

相关文章:

c# - 设计附加基础应用程序

c# - 在检查器中显示数组 (Unity)

c# - 从 EntityFramework 的 `DbContext` 获取数据库类型

entity-framework - 如何让 Appstats 同时显示小操作和读取操作?

c# - 使用自定义 View 基础在 Razor View 中调用异步函数

c# - 显示GIF后,C#Windows Form中的WMP停止播放声音

c# - 使用 group by 和 having 的 Linq 查询

c# - 已编译的 Dynamic Linq Func<> *没有*参数的缓存委托(delegate)?

c# - 在 LINQ 中按平均值分组

c# - 从ViewModel访问数据库是否违反了MVC原则?