.NET实体框架 - IEnumerable VS. IQueryable的

jus*_*uzz 23 ienumerable frameworks entity entity-framework iqueryable

请看这行代码.这是一个存储过程的调用,它返回一个ObjectResult<long?>.为了提取长值,我添加了Select:

dbContext.FindCoursesWithKeywords(keywords).Select(l => l.Value);
Run Code Online (Sandbox Code Playgroud)

基于intellisense,这个Select返回IEnumerable<long>.

我不确定我是否在某处读过它或者只是习惯了这个假设 - 我一直认为当EF API返回IEnumerable(而不是IQueryable)时,这意味着结果已经实现.意味着他们已从数据库中撤出.

我今天发现我错了(或者这可能是个错误?).我一直收到错误

"不允许新事务,因为会话中还有其他线程在运行"

基本上,此错误告诉您在db阅读器仍在读取记录时尝试保存更改.

最终我通过(我认为是一个长镜头)解决了这个问题并增加ToArray()了实现IEnumerable<long>......

那么 - 底线 - 我应该期望IEnumerableEF的结果包含尚未实现的结果吗?如果是,那么有没有办法知道一个IEnumerable是否已实现?

如果这是'duhhh'问题之一,感谢并道歉...... :)

Lad*_*nka 29

IQueryable在使用Linq-to-entities时使用=您在应用程序中构建声明性LINQ查询,LINQ提供程序将其解释为SQL并在服务器上执行.一旦查询被执行(迭代),它将变为IEnumerable,并且对象将根据需要实现迭代=不立即.

一旦调用存储过程,就不会使用Linq-to-entities,因为应用程序中没有内置声明性查询.查询/ SQL已存在于数据库服务器上,您只是在调用它.这将返回IEnumerable但是它不会立即实现所有结果.结果将实现为迭代.当您明确要求获取对象时,这是数据库游标/或.NET数据读取器的原理.

所以如果你打这样的话:

foreach (var keyword in dbContext.FindCoursesWithKeywords(keywords)
                                 .Select(l => l.Value))
{
    ...   
}
Run Code Online (Sandbox Code Playgroud)

您正在逐个学习课程(顺便说一句.如果您只对关键词感兴趣,为什么要加载整个课程?).在您完成或中断循环之前,您的数据阅读器将被打开以获取记录.

如果您改为这样称呼:

foreach (var keyword in dbContext.FindCoursesWithKeywords(keywords)
                                 .ToList() // or ToArray 
                                 .Select(l => l.Value))
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

您将强制查询立即实现所有结果,循环将在内存中执行而不是打开数据库读取器.

之间的差异IEnumerableIQueryable不在数据是如何获取的,因为这样IQueryableIEnumerable.不同之处在于支持构造(必须实现这些接口).


Cod*_*aos 11

处理一个IEnumerable<T>意味着所有进一步的操作将在C#代码中发生,即linq-to-objects.这并不意味着查询已经执行.

一旦降级为linq-to-objects,此时剩下的所有数据都需要从数据库中获取并发送到.net.这会降低性能急剧(例如数据库索引不会被LINQ到对象使用),但在另一方面LINQ到对象更灵活,因为它可以代替由什么限制执行任意的C#代码您的linq提供程序可以转换为SQL.

A IEnumerable<T>既可以是延迟查询,也可以是已实现的数据.标准linq运算符通常是延迟的,并且ToArray()/ ToList()始终具体化.