实体框架+ LINQ +"包含"==超慢?

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 (...)针对每一行计算的子查询.

  • 我不明白:(我同意延迟执行参数*如果*第二个查询将是LINQ to Objects.但显然它是LINQ to Entities.第二个查询被翻译成SQL(只有一次),然后是执行SQL.对于表达式,*声明*类型的`StudentIds`很重要,而不是运行时类型.如果使用`IEnumerable <int>`或者使用`var`(=`IQueryable <int >`).在第一种情况下,第一个查询执行一次,第二个查询转换为`IN`,第二种情况是`exists(子查询)`.我不知道1000个查询来自哪里. (2认同)