使用 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的区别
asp.net mvc中使用linq to sql查询数据集(IQueryable类型) 怎么用foreach循环去数据集的数据?