我有一个 ASP.NET 页面,其中包含一堆需要填充的控件(例如下拉列表)。
我想单次访问数据库并带回多个记录集,而不是为每个控件进行往返。
我可以带回 DataSet 中的多个表,或者我可以带回 DataReader 并使用“.NextResult”将每个结果集放入自定义业务类中。
使用 DataReader 方法可能会看到足够大的性能优势,还是应该只使用 DataSet 方法?
您通常如何处理此问题的任何示例将不胜感激。
我有一个 ASP.NET 页面,其中包含一堆需要填充的控件(例如下拉列表)。
我想单次访问数据库并带回多个记录集,而不是为每个控件进行往返。
我可以带回 DataSet 中的多个表,或者我可以带回 DataReader 并使用“.NextResult”将每个结果集放入自定义业务类中。
使用 DataReader 方法可能会看到足够大的性能优势,还是应该只使用 DataSet 方法?
您通常如何处理此问题的任何示例将不胜感激。
然后我认为更好的是使用DataReader。
别的
然后我认为更好的是使用DataSet。
我希望我是对的。
如果您的存储过程返回多个集合,请使用 DataReader.NextResult 前进到下一个数据块。通过这种方式,您可以获得所有数据,将其加载到您的对象中,并尽快关闭阅读器。这将是获取数据的最快方法。
看到即使已经有很多好的答案也没有标记答案,我想我也会添加两位。
我会使用 DataReaders,因为它们要快得多(如果性能是你的事,或者你需要尽可能多的东西)。我从事的大多数项目在每个表中都有数百万条记录,性能是一个问题。
有人说跨层发送 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 存在一些问题,这通常是代码清晰度、编译时检查等。您可以为数据读取器使用包装类,实际上您可以将 DataReaders 与它们一起使用 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)
{
}
}
我有一篇博文(上面的链接),其中包含更多细节,我将为这些和其他 DataAccess 层代码制作源代码生成器。
您可以对 DataTables 使用相同的技术(代码生成器生成代码),因此您可以将它们视为强类型 DataTable,而不会产生 VS.NET 开箱即用的开销。
请记住,包装类只有一个实例。所以你不会创建一个类的数百个实例只是为了把它扔掉。
始终将您的数据放入为特定用途定义的类中。不要传递 DataSet 或 DataReaders。
Map the DataReader to intermediate objects and then bind your controls using those objects. It can be ok to use DataSets in certain circumstances, but those are few and far between when you have strong reasons for "just getting data". Whatever you do, don't pass a DataReader to your controls to bind off of (not that you said that you were considering that).
My personal preference would be to use an ORM, but if you are going to hand roll your data access, by all means I think you should prefer mapping DataReaders to objects over using DataSets. Using the .NextResult as a way to limit yourself from hitting the database multiple times is a double edged sword however so choose wisely. You will find yourself repeating yourself if you try to create procs that always grab exactly what you need using only one call to the database. If your application is only a few pages, it is probably OK, but things can get out of control quickly. Personally I'd rather have one proc per object type and then hit the database multiple times (once for each object type) in order to maximize maintainability. This is where an ORM shines because a good one will generate Sql that will get you exactly what you want with one call in most cases.
无论您是获取单个结果集还是多个结果集,共识似乎是使用 DataReader 而不是 DataSet。
关于你是否应该打扰多个结果集,智慧是您不应该这样做,但我可以设想出该规则的合理例外类别:(紧密)相关的结果集。您当然不希望添加一个查询来为在应用程序的数百甚至数十个页面上重复的下拉列表返回相同的选择集,但可以合理地组合几组狭窄使用的选项。例如,我目前正在创建一个页面来显示一批 ETL 数据的几组“差异”。这些查询都不可能在其他地方使用,因此将它们封装为单个“获取差异”存储过程会很方便。另一方面,
如果您对更新或删除从数据库中获取的记录不感兴趣,我建议您使用 DataReader。基本上 DataSet 在内部使用多个 Datareader,因此 DataReader 应该会给您带来良好的性能优势。
我已经使用了一种对所有调用都使用 DataReaders 的方法,我注意到性能显着提高,尤其是在我加载下拉列表和其他类似的简单项目时。
就个人而言,使用多个下拉菜单,我通常会提取单个数据块来获取它,而不是说一个返回 5 个结果集的存储过程。
查看 .NET 2.0 及更高版本可用的 TableAdapter。他们所做的是为您提供强类型 DataTable 的强度,并允许您将 Fill 方法映射到它,该方法将使用 DataReader 来加载它。您的填充方法可以是现有的存储过程、您自己的 AdHoc SQL,甚至可以让向导为您生成 AdHod 或存储过程。
您可以通过在项目中启动一个新的 XSD DataSet 对象来找到它。对于不仅仅用于查找的表,您还可以将插入/更新/删除方法映射到 TableAdapter。