使用 forEach 迭代 IQueryable 与调用 ToList() 然后执行 forEach - 性能

Posted

技术标签:

【中文标题】使用 forEach 迭代 IQueryable 与调用 ToList() 然后执行 forEach - 性能【英文标题】:Iterating over IQueryable with forEach vs. calling ToList() and then doing forEach - performance 【发布时间】:2016-10-11 05:19:59 【问题描述】:

我一直在看一门课程,作者提到如果你有一些IQueryable,请说:

var someQuery = dbContext.Table.Where(x => x.Name == "Mark");

然后尝试使用foreach 迭代someQuery 的结果,它会保持数据库连接打开直到整个foreach 结束,并且许多初学者开发人员都会犯这个错误并在foreach。相反,她建议先调用ToList(),然后在内存集合上调用foreach

我找不到任何关于此的参考,实体框架保持数据库打开,直到foreach 循环停止。我如何评估这是否是真正的性能方面?

【问题讨论】:

我有found that out the hard way 是的,它会保持连接打开,直到遇到 .toList() 连接保持打开状态,直到IQueryable 的枚举完成。这将发生在 foreach 循环结束或调用 .ToList() 时。但是,尚不清楚以一种或另一种方式执行此操作会导致性能损失。长时间保持数据库连接打开肯定存在问题 - 您通常希望尽快释放连接。 【参考方案1】:

应用于IQueryable<T>ToList() 方法是您可以在带有.NET 反编译器的System.core.dll 中看到的扩展方法; 如果源是ICollection<T>,则该方法复制“私有数组”,否则从源执行foreach 循环。

IQueryable<T> 的真正实现是System.Data.Entity.Infrastructure.DbQuery<TResult>,而不是ICollection<T>

所以,ToList() 基本上是使用foreach 复制源代码。

现在,这取决于实现,但以 EF 核心为例,有一个 Enumerator class,您可以在其中看到连接 (IRelationalConnection) 在处理类时关闭。 另一位answer 和女士documentation 表示,您应该尽快关闭连接以释放连接并使其在连接池中可用。

为每个唯一的连接字符串创建一个连接池。创建池时,会创建多个连接对象并将其添加到池中,以满足最小池大小要求。连接会根据需要添加到池中,直至指定的最大池大小(默认值为 100)。当连接被关闭或释放时,连接会被释放回池中。

因此,如果您正在执行复杂的操作或执行其他查询或 foreach 中的其他任何内容,则首选 ToList。

【讨论】:

以上是关于使用 forEach 迭代 IQueryable 与调用 ToList() 然后执行 forEach - 性能的主要内容,如果未能解决你的问题,请参考以下文章

2.3 IQueryable与 IEnumerable的区别

IEnumerable,IQueryable之前世今生

asp.net mvc中使用linq to sql查询数据集(IQueryable类型) 怎么用foreach循环去数据集的数据?

datatable怎么转化为iqueryable

在循环中使用 IQueryable 的实体框架 WHERE OR 查询

IQueryable、ICollection、IList 和 IDictionary 接口之间的区别