如何使用Linq to Entities处理大型结果集?

我有一个相当复杂的linq to entity查询,我在网站上显示。 它使用分页,所以我从来没有一次下载超过50条记录进行显示。

但我也想让用户选择将完整结果导出到Excel或其他一些文件格式。

我担心的是,一次可能会有大量的记录被加载到内存中来执行此操作。

有没有办法一次处理linq结果集1记录,就像你可以使用datareader一样,所以一次只能在内存中保存1条记录?

我已经看到了一些建议,如果你使用foreach循环对linq查询进行枚举,记录将不会立即全部读入内存并且不会过度使用服务器。

有没有人有我可以阅读的东西的链接来validation这一点?

我很感激任何帮助。

谢谢

ObjectContext设置为MergeOption.NoTracking (因为它是只读操作)。 如果您使用相同的ObjectContext来保存其他数据,请从上下文中分离对象 。

怎么分开

 foreach( IQueryable) { //do something objectContext.Detach(object); } 

编辑 :如果您使用NoTracking选项,则无需分离

编辑2 :我写信给Matt Warren关于这个场景。 在他的批准下,我在这里发布相关的私人通信

SQL服务器的结果可能甚至都不是由服务器生成的。 查询已在服务器上启动,第一批结果将传输到客户端,但在客户端请求继续读取之前,不会再生成(或将它们缓存在服务器上)。 这就是所谓的’firehose cursor’模式,有时也称为流式传输。 服务器正在尽可能快地发送它们,并且客户端正在尽可能快地读取它们(您的代码),但是下面有一个数据传输协议,需要客户端确认继续发送更多数据。

由于IQueryableinheritance自IEnumerable ,我相信发送到服务器的基础查询将是相同的。 但是,当我们执行IEnumerable.ToList() ,底层连接使用的数据读取器将开始填充对象,对象被加载到app域中,并且可能内存不足,这些对象无法处理。

当您使用foreachIEunmerable ,数据读取器一次读取一个SQL结果集,创建然后处理对象。 底层连接可能以块的forms接收数据,并且在读取所有块之前可能不会向SQL Server发送响应。 因此,您不会遇到“内存不足”exception

编辑3

当您的查询正在运行时,您实际上可以打开SQL Server“活动监视器”并查看查询,任务状态为SUSPENDED,等待类型为Async_network_IO – 实际上表明结果在SQL Server网络缓冲区中。 你可以在这里和这里阅读更多相关信息

查看LINQ查询的返回值。 它应该是IEnumerable <> ,它一次只加载一个对象。 如果你使用类似.ToList()东西,它们都将被加载到内存中。 只需确保您的代码不会维护列表或一次使用多个实例,您就可以了。

编辑:添加人们对foreach所说的内容……如果您执行以下操作:

 var query = from o in Objects where o.Name = "abc" select o; foreach (Object o in query) { // Do something with o } 

查询部分使用延迟执行( 请参阅示例 ),因此对象尚未在内存中。 foreach遍历结果,但一次只获取一个对象。 query使用IEnumerator ,它有Reset()MoveNext() 。 foreach每轮调用MoveNext(),直到没有结果。