在 ASP.NET 中拉多个记录集时的 DataReader 或 DataSet

Posted

技术标签:

【中文标题】在 ASP.NET 中拉多个记录集时的 DataReader 或 DataSet【英文标题】:DataReader or DataSet when pulling multiple recordsets in ASP.NET 【发布时间】:2010-09-15 20:50:51 【问题描述】:

我有一个 ASP.NET 页面,其中包含一堆需要填充的控件(例如下拉列表)。

我想单次访问数据库并带回多个记录集,而不是为每个控件进行往返。

我可以带回一个 DataSet 中的多个表,或者我可以带回一个 DataReader 并使用“.NextResult”将每个结果集放入一个自定义业务类中。

我是否会看到使用 DataReader 方法获得足够大的性能优势,还是应该只使用 DataSet 方法?

任何您通常如何处理此问题的示例将不胜感激。

【问题讨论】:

【参考方案1】:
    如果您有超过 1000 条记录要从您的数据库中获取。 如果你不是很感兴趣 自定义存储和自定义分页 "对于 GridView" 如果您的服务器存在内存压力。 如果连接没有问题 每次该页面时您的数据库 调用。

那我觉得用DataReader比较好。

其他

    如果您从数据库中获取的记录少于 1000 个。 如果您对 存储和分页“对于 GridView" 如果您的服务器没有内存 压力。 如果您想连接到您的 数据库只需一次并获取 缓存的好处。

那我觉得用DataSet比较好。

我认为我是对的。

【讨论】:

【参考方案2】:

始终将您的数据放入为特定用途定义的类中。不要传递 DataSet 或 DataReaders。

【讨论】:

“总是”是指“有时?” 别这样!不要让他们四处走动! 颤抖 来自来之不易的经验。从来没有,从来没有。除非你正在做一个非常扔掉的原型。我生活在到处乱飞的不可重用数据集。一开始,它们似乎是个好主意(嘿,这是一个轻量级的开发!!),但是一旦您尝试在其上维护或构建/扩展它们,它们就会成为一个拖累。多花 30 分钟为数据编写类几乎是立竿见影的(单元测试任何人?)。【参考方案3】:

如果您的存储过程返回多个集合,请使用 DataReader.NextResult 前进到下一个数据块。通过这种方式,您可以获得所有数据,将其加载到您的对象中,并尽快关闭阅读器。这将是获取数据的最快方法。

【讨论】:

太棒了,谢谢!我一直想知道你能不能做到这一点——我一定过着隐蔽的生活!【参考方案4】:

虽然已经有很多好的答案,但看到还没有标记答案,我想我也应该添加两位。

我会使用 DataReader,因为它们的速度要快得多(如果性能是你的事,或者你需要尽可能多的东西)。我从事的大多数项目的每个表中都有数百万条记录,性能是一个问题。

有人说跨层发送 DataReader 不是一个好主意。我个人不认为这是一个问题,因为“DbDataReader”在技术上与数据库无关(或不必)。也就是说,您无需数据库即可创建 DbDataReader 的实例。

我为什么这样做是出于以下原因: 您经常(在 Web 应用程序中)生成 html、Xml 或 JSON 或其他一些数据转换。那么,为什么从 DaraReader 转到某个 POCO 对象只是为了将其转换回 XML 或 JSON 并将其发送到网络上。这种过程通常需要 3 次转换和对象实例化的引导加载,但几乎立即将它们丢弃。

在某些情况下这很好或无能为力。我的数据层通常为系统中的每个存储过程提供两种方法。一个返回 DbDataReader,另一个返回 DataSet/DataTable。返回 DataSet/DataTable 的方法调用返回 DbDataReader 的方法,然后使用 DataTable 的“Load”方法或适配器来填充数据集。有时您需要 DataSet,因为您可能必须以某种方式重新调整数据的用途,或者您需要触发另一个查询,并且除非您启用了 MARS,否则您无法打开 DbDataReader 并触发另一个查询。

现在使用 DbDataReader 或 DataSet/DataTable 存在一些问题,通常是代码清晰度、编译时检查等。您可以为数据读取器使用包装类,实际上您可以将 DataReader 与 IEnumerable 一起使用。真的很酷的能力。因此,您不仅可以获得强大的类型和代码可读性,还可以获得 IEnumerable!

所以一个类可能看起来像这样。

public sealed class BlogItemDrw : BaseDbDataReaderWrapper

    public Int64 ItemId  get  return (Int64)DbDataReader[0];  
    public Int64 MemberId  get  return (Int64)DbDataReader[1];  
    public String ItemTitle  get  return (String)DbDataReader[2];  
    public String ItemDesc  get  if (DbDataReader[3] != DBNull.Value) return (String)DbDataReader[3]; else return default(String);  
    public DateTime ItemPubdate  get  return (DateTime)DbDataReader[4];  
    public Int32 ItemCommentCnt  get  return (Int32)DbDataReader[5];  
    public Boolean ItemAllowComment  get  return (Boolean)DbDataReader[6];  
    public BlogItemDrw()
      :base()
    
    

    public BlogItemDrw(DbDataReader dbDataReader)
      :base(dbDataReader)
    
    

DataReader Wrapper

我有一篇博文(上面的链接),其中包含更多细节,我将为这些和其他 DataAccess 层代码制作源代码生成器。

您可以对 DataTables 使用相同的技术(代码生成器生成代码),这样您就可以将它们视为强类型 DataTable,而不会产生 VS.NET 开箱即用的开销。

请记住,包装类只有一个实例。因此,您创建一个类的数百个实例并不是为了将其丢弃。

【讨论】:

这是一个非常有趣(且详细)的答案。 尝试将其提升到一个新的水平,并使用返回 IEnumerable<IDataRecord> 的迭代器块包装数据读取器。让您立即将查询结果与 linq 一起使用。 乔尔,如果你看看我的博客文章,你会发现我也展示了如何做到这一点:)。事实上,我使用我的项目更进一步,您可以使用与 POCO 相同的类,因此您可以获得 List(当您需要时)或将其用作包装器。【参考方案5】:

将 DataReader 映射到中间对象,然后使用这些对象绑定您的控件。在某些情况下使用 DataSet 是可以的,但是当您有充分的理由“只是获取数据”时,这些情况很少见。无论您做什么,都不要将 DataReader 传递给您的控件以绑定(不是您说您正在考虑)。

我个人的偏好是使用 ORM,但如果您打算手动访问数据,我认为您应该更喜欢将 DataReaders 映射到对象而不是使用 DataSets。使用 .NextResult 来限制自己多次访问数据库是一把双刃剑,但请明智地选择。如果您尝试创建只使用一次数据库调用即可始终准确获取所需内容的过程,您会发现自己在重复自己。如果您的应用程序只有几页,那可能没问题,但事情很快就会失控。就我个人而言,我宁愿每个对象类型有一个 proc,然后多次访问数据库(每种对象类型一次),以最大限度地提高可维护性。这就是 ORM 大放异彩的地方,因为一个好的 ORM 将生成 Sql,在大多数情况下,只需一次调用即可获得您想要的。

【讨论】:

我认为使用数据集和数据表的极端开销总是会让它们成为任何事情的糟糕选择,但当你觉得懒惰时;)aspnet.4guysfromrolla.com/articles/050405-1.aspx 我大体上同意,这就是为什么我通常映射到中间对象。我宁愿承担映射到 DTO 的开销,也不愿在绑定到多个控件时保持连接打开。通过层传递 DataReader 也是邪恶的,因此一般设计准则反对绑定到 DataReader 澄清一下:我的优先顺序是 1) 绑定到对象 2) 绑定到 DataSet/Table/Adapter 3) 直接绑定到 datareader(不要这样做!)。【参考方案6】:

如果您对更新或删除从数据库中获取的记录不感兴趣,我建议您使用 DataReader。基本上 DataSet 内部使用了多个 Datareader,因此 DataReader 应该会给您带来良好的性能优势。

【讨论】:

【参考方案7】:

在几乎所有情况下DataReaders are the best solution 用于从数据库中读取。 DataReaders 是 faster 并且比 DataTables 或 DataSets 需要更少的内存。

此外,DataSets 通常会导致 OO 模型为broken 的情况。传递关系数据/模式而不是知道如何操作该数据的对象并不是非常面向对象的。

因此,出于可扩展性、可伸缩性、模块化和性能原因,如果您认为自己是真正的程序员™,请始终使用 DataReaders ;)

查看链接以获取有关两者在实践和理论中的事实和讨论。

【讨论】:

【参考方案8】:

无论您是获取单个结果集还是多个结果集,共识似乎是使用 DataReader 而不是 DataSet。

关于你是否应该曾经为多个结果集烦恼,智慧是你不应该,但我可以设想出该规则的合理例外类别:(严格)相关的结果集。您当然不希望添加一个查询来为在您的应用程序的数百甚至数十个页面上重复的下拉列表返回相同的选择集,但是可以合理地组合几组狭窄使用的选项。例如,我目前正在创建一个页面来显示一批 ETL 数据的几组“差异”。这些查询都不可能在其他地方使用,因此将它们封装为单个“获取差异”存储过程会很方便。另一方面,与解决 ORM 或手动数据访问代码的自然 one-sproc-one-result-set 架构相比,这样做的更好性能可能微不足道。

【讨论】:

【参考方案9】:

我使用了一种对所有调用都使用 DataReaders 的方法,我注意到性能显着提升,尤其是在我加载下拉列表和其他类似的简单项目时。

对于多个下拉菜单,我通常会去提取单个数据块来获取它,而不是说一个返回 5 个结果集的存储过程。

【讨论】:

【参考方案10】:

查看可用于 .NET 2.0 及更高版本的 TableAdapter。他们所做的是为您提供强类型 DataTable 的强度,并允许您将 Fill 方法映射到它,该方法将使用 DataReader 加载它。您的填充方法可以是现有的存储过程、您自己的 AdHoc SQL,甚至可以让向导为您生成 AdHod 或存储过程。

您可以通过在项目中启动一个新的 XSD DataSet 对象来找到它。对于不仅仅用于查找的表,您还可以将插入/更新/删除方法映射到 TableAdapter。

【讨论】:

以上是关于在 ASP.NET 中拉多个记录集时的 DataReader 或 DataSet的主要内容,如果未能解决你的问题,请参考以下文章

asp.net 使用存储过程时参数为空时的处理

如何在 ASP.NET Core API 控制器中获取多个表的记录

Asp.Net Core Log4Net 配置分多个文件记录日志(不同日志级别)

如何在 ASP.NET Core 2.0 中设置多个身份验证方案?

如何在 Asp.net MVC C# 中使用 Linq 从多个表中选择具有最大计数值的记录

我应该如何将多个参数传递给 ASP.Net Web API GET?