65.9K
CodeProject 正在变化。 阅读更多。
Home

从 ADO.NET DataSet 继承以创建您自己的业务对象

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (10投票s)

2004 年 10 月 17 日

6分钟阅读

viewsIcon

191146

downloadIcon

3151

扩展生成的强类型 DataSet,向真实的 DataSet 对象添加您自己的业务特定功能。绑定到表单和更新到数据库都由 .NET Framework 完成。这是基于 Shawn Wildermuth(又名 ADO Guy)的 DataSetGenerator 构建的。

Sample Image - InheritFromDataSet.jpg

引言

如果能将数据库模型拖到 Visual Studio 设计器中,向生成的 DataSet 添加自己的代码,并将业务对象绑定到表单,就像内置的 ADO.NET DataSet 一样,那该多好?

任何尝试创建 Typed DataSet 的子类的人都会发现这是非常有限的。您可以创建 DataSet 本身的子类,但不可能创建 DataTableDataRow 的可用子类。

如果您查看 MSDataSetGenerator 生成的代码,您会发现大多数有趣的东西都不可重写。Shawn Wildermuth,自称 The ADO Guy,创建了他自己的 DataSetGenerator 来解决这个问题。您可以在 他的网站 上找到关于这个强大工具的原理和原因的描述。为了能够理解本文的其余部分,我强烈建议您先阅读他的解释。

不过,请使用我的生成器版本,因为我做了一些小改动,使得可以将基类放在与子类不同的程序集中。

安装 DataSet 生成器

当您将 DataSet 添加到您的 Visual Studio 项目时,您会得到一个 .xsd 文件,其中 MSDataSetGenerator 作为关联的自定义工具。在解决方案资源管理器中选择该文件并查看属性网格。要创建您可以继承的 Typed DataSet,您必须将其替换为 AGDataSetGenerator。但首先,您必须告诉 Visual Studio 可以在哪里找到此工具。为此,您必须运行 reg.cmdVSDataSetGenerators.reg。如果您想将 AGDataSetGenerator.dll 放在不同的文件夹中,您可能需要更改 reg.cmd。我没有制作一个漂亮的安装程序,但如果有人愿意为我制作一个,请发给我,我会将其添加到此页面。

创建 Base DataSet

创建可继承的 DataSet 几乎与普通的 Typed DataSet 完全相同。只需将 DataSet 添加到您的项目,然后将您的表从服务器资源管理器拖到设计图面上。为了能够添加注释,您可以在 <xs:schema> 标签中添加 xmlns:codegen="urn:schemas-microsoft-com:xml-msprop"。要了解此内容,请在 Visual Studio 帮助文件中搜索“带有 Typed DataSet 的注释”。

这些是我更改 XSD 文件以创建我的基类的两行代码

<xs:element name="Orders" codegen:typedName="OrderBase" 
                 codegen:typedPlural="OrdersBase">
<xs:element name="OrderDetails" codegen:typedName="OrderDetailBase" 
                 codegen:typedPlural="OrderDetailsBase">

创建 DataAdapter

DataAdapter 需要托管在某个地方。这可以是一个 Form 或一个 Component。在我的应用程序中,我为每组表创建一个单独的 Component。在示例中,我有一个 Component 包含 OrdersOrderDetailsDataAdapter。如果我想使用不同的选择标准从数据库填充 DataSet,我会为同一个表使用多个 DataAdapter

我有我自己版本的乐观并发控制。为此,我必须更改由 DataAdapter 向导生成的存储过程。更改存储过程后,我必须重新运行向导,以使用新的存储过程更新 DataAdapter

UPDATE Orders

SET Customer = @Customer, OrderDate = @OrderDate

WHERE (Id = @Original_Id) AND (Customer = @Original_Customer) 
AND (OrderDate = @Original_OrderDate) AND (Timestamp = @Original_Timestamp);

... 变成

UPDATE Orders

SET Customer = @Customer, OrderDate = @OrderDate

WHERE (Id = @Id) AND (Timestamp = @Timestamp);

这些是要遵循的步骤

  1. 通过将表从服务器资源管理器拖到 Component 的设计图面上来创建 DataAdapter
  2. 选择菜单选项“配置数据适配器”。
  3. 选择“创建新的存储过程”。
  4. 编辑生成的存储过程:删除 @Original_... 参数并通过 Timestamp 字段执行乐观并发。
  5. 再次转到“配置数据适配器”,并使用更改的存储过程刷新数据适配器(“使用现有存储过程”)。

从 Base DataSet 派生

DataSet 生成器的大部分功能都由 Shawn Wildermuth 涵盖。他描述了您如何可以选择从基本 DataTable 派生,或直接使用基类。他解释了您必须在每个子类中放入的几行额外代码以将所有内容粘合在一起。

我将在此处解释的内容是

  • 处理标识字段
  • 乐观并发

标识字段:SQL Server 和 ADO 都支持自动编号。如果它们都从 1 开始并以 1 递增,那么在将 DataSet 更新到数据库时,您很可能会遇到冲突。为了防止这种情况,只需在 XSD 设计器中将 AutoIncrementSeedAutoIncrementStep 都设置为 -1。这就是您在 XML 中得到的结果

<xs:element name="Id" msdata:ReadOnly="true" msdata:AutoIncrement="true" 
   type="xs:int" msdata:AutoIncrementSeed="-1" msdata:AutoIncrementStep="-1" />

DataSet 中的新记录会获得一个临时 ID 值。当它们被 INSERT 到数据库中时,DataAdapter 会将数据库生成的 ID 值写入内存中的 DataRow。这一切都很好,但如果您想将 DataSet 与与服务器通信的服务分开,您需要一个临时 DataSet 来仅保存更改。要将更改合并回原始 DataSet,您不能使用 ID 字段,因为它们不再与 INSERT 的行匹配。为了规避这个问题,您必须向 DataSet 添加一个临时标识字段。这就是我们在 DataSet 中包含此代码的原因

Protected Overrides Sub InitClass()
 MyBase.InitClass()
 Dim Column As DataColumn
 For Each Table As DataTable In Me.Tables
  Column = New DataColumn("ClientSideID", GetType(System.Int32), Nothing,
     System.Data.MappingType.Element)
  Column.AutoIncrement = True
  Column.ColumnMapping = MappingType.Hidden
  Table.Columns.Add(Column)
 Next
End Sub

DataSetUpdate 方法中,我们从 DataService 获取临时 DataSetClientSideID 用于将原始行与更新后的行进行匹配。您可以在 OrderSet.vb 中找到此代码。

乐观并发通过向数据库中的每个表添加一个 Timestamp 字段来处理。已被其他用户更改的记录的更新将失败。如果您已将 DataAdapter 上的 ContinueUpdateOnError = True 设置为 True,则其他记录的更新将成功。在 update 操作之后,您可以检查由 DataTable.GetErrors 返回的行。

使用 DataSets 的想法

我在这里提出的框架解决了一些常见问题,例如将数据库字段与对象成员、标识字段、乐观并发进行映射。它还分离了对象和与数据库的通信。下一步可能是将包含 DataAdapterService 对象隐藏在 Web 服务中。我尝试过,效果非常好。

喜欢设计模式的人可以应用工厂模式来动态构建 DataSetDataAdapter。您还可以创建自己的 DataAdapterGenerator 或自己的 StoredProcedureGenerator。在我看来,最好不要使用太多的代码生成器。您最终可能会在调整生成器上花费更多时间,而不是构建您为此付费的软件。

摘要

  • 尽可能多地使用 Visual Studio 的内置设计器工具。
  • 节省 O/R 映射工具——它们也不完美。
  • 尽量限制必要的手动更改(例如,存储过程)的数量。
  • 手动进行必要的更改,不要尝试重新发明轮子。

历史

  • 2004 年 10 月 27 日:初始版本

许可证

本文没有明确的许可证,但可能在文章文本或下载文件中包含使用条款。如有疑问,请通过下面的讨论板联系作者。作者可能使用的许可证列表可在此处找到。

© . All rights reserved.