【问题标题】:Return deferred data from SqlDataReader从 SqlDataReader 返回延迟数据
【发布时间】:2015-02-23 17:13:33
【问题描述】:

问题:我有数百万行来自数据库要处理。

我需要实现一个方法,该方法将返回数据库行的“流”(?)。 我不想一次将它们全部加载到内存中。

我正在考虑返回一个懒惰的IEnumerable<Record> 并使用yield。 该方法将使用SqlDataReader 处理加载连续记录。

但是当客户在我的IEnumerable 上调用.Count() 时会发生什么?计算所有记录意味着需要全部获取。

有没有什么好的现代方法可以返回一个不将所有对象都存储在内存中的对象流,只需一个一个地处理?我的方法应该返回一个记录流。

反应式扩展似乎可以为我解决问题,但我从未使用过。

有什么想法吗?

谢谢

【问题讨论】:

    标签: c# .net lazy-loading lazy-evaluation sqldatareader


    【解决方案1】:

    首先,为什么要重新发明轮子? Entity Framework 让做这样的事情变得更容易,并为你添加了所有的抽象。 DbContext 对象上的 DbSet<TEntity> 实现了 IQueryable<TEntity>IEnumerable<T>,因此您可以:

    • 当您需要计算记录数(或其他一些聚合函数)时,使用扩展方法执行 Count()(带和不带 lamda 过滤器参数)
    • 您可以将它们作为 IEnumerable 循环访问,该 IEnumerable 每次从连接调用 MoveNext 方法时都会打开一个连接并读取 1 条记录。
    • 如果您确实想一次将所有内容加载到内存中(我知道您不是根据您的描述),您可以调用扩展方法 ToList 或 ToArray。

    如果您坚持使用 ADO.NET 并手动执行此操作(我了解遗留代码并不总是可以选择使用 EF),那么从连接中打开数据读取器是最好的方法。这将读取每条下一条记录以及对方法 Read() 的每个相应调用,这是读取数据库中记录的最便宜的方法。

    如果您想要一个计数,那么我建议您编写一个新的 sql 查询,该查询返回使用类似于 Sql 的数据库服务器上执行的计数

    SELECT COUNT(field) FROM table 
    

    因为这是最佳做法。不要通过一些自定义工作来迭代和总结来自阅读器的所有记录以在内存中执行求和,这将浪费资源,更不用说创建没有任何好处的复杂代码了。

    【讨论】:

    • 我可能是错的,但我读到只有在 EF 6 中,他们才最终添加了默认打开底层数据读取器的功能。我针对数据读取器测试了 EF 5,当从 100 万个表中拉出 5 万行的页面时,数据读取器的性能提升了大约 33%。 EF 6 与数据读取器的时间几乎完全相同。
    • @NightOwl888 - 有趣。我们仍然有一个使用 EF5 的旧版应用程序,但除此之外,我对这个版本没有做太多。我试着看,但我能发现的是框架的初始加载时间在版本之间得到了改善。如果您找到链接,我将有兴趣了解更多信息。 ?也可能是因为 EF6 生成了更高效的查询?
    【解决方案2】:

    用于count查询db,并返回给用户。

    另一方面,您只需要为 ICollection 实现计数,IEnumerable 不需要。只需返回 IEnumerable 以对记录进行迭代。

    请注意您正确处理了与数据库的连接。

    【讨论】:

      猜你喜欢
      • 2011-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多