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






4.67/5 (10投票s)
2004 年 10 月 17 日
6分钟阅读

191146

3151
扩展生成的强类型 DataSet,向真实的 DataSet 对象添加您自己的业务特定功能。绑定到表单和更新到数据库都由 .NET Framework 完成。这是基于 Shawn Wildermuth(又名 ADO Guy)的 DataSetGenerator 构建的。
引言
如果能将数据库模型拖到 Visual Studio 设计器中,向生成的 DataSet 添加自己的代码,并将业务对象绑定到表单,就像内置的 ADO.NET DataSet
一样,那该多好?
任何尝试创建 Typed DataSet
的子类的人都会发现这是非常有限的。您可以创建 DataSet
本身的子类,但不可能创建 DataTable
或 DataRow
的可用子类。
如果您查看 MSDataSetGenerator
生成的代码,您会发现大多数有趣的东西都不可重写。Shawn Wildermuth,自称 The ADO Guy,创建了他自己的 DataSetGenerator
来解决这个问题。您可以在 他的网站 上找到关于这个强大工具的原理和原因的描述。为了能够理解本文的其余部分,我强烈建议您先阅读他的解释。
不过,请使用我的生成器版本,因为我做了一些小改动,使得可以将基类放在与子类不同的程序集中。
安装 DataSet 生成器
当您将 DataSet
添加到您的 Visual Studio 项目时,您会得到一个 .xsd 文件,其中 MSDataSetGenerator
作为关联的自定义工具。在解决方案资源管理器中选择该文件并查看属性网格。要创建您可以继承的 Typed DataSet
,您必须将其替换为 AGDataSetGenerator
。但首先,您必须告诉 Visual Studio 可以在哪里找到此工具。为此,您必须运行 reg.cmd 和 VSDataSetGenerators.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 包含 Orders
和 OrderDetails
的 DataAdapter
。如果我想使用不同的选择标准从数据库填充 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);
这些是要遵循的步骤
- 通过将表从服务器资源管理器拖到 Component 的设计图面上来创建
DataAdapter
。 - 选择菜单选项“配置数据适配器”。
- 选择“创建新的存储过程”。
- 编辑生成的存储过程:删除
@Original_...
参数并通过Timestamp
字段执行乐观并发。 - 再次转到“配置数据适配器”,并使用更改的存储过程刷新数据适配器(“使用现有存储过程”)。
从 Base DataSet 派生
DataSet
生成器的大部分功能都由 Shawn Wildermuth 涵盖。他描述了您如何可以选择从基本 DataTable
派生,或直接使用基类。他解释了您必须在每个子类中放入的几行额外代码以将所有内容粘合在一起。
我将在此处解释的内容是
- 处理标识字段
- 乐观并发
标识字段:SQL Server 和 ADO 都支持自动编号。如果它们都从 1
开始并以 1
递增,那么在将 DataSet
更新到数据库时,您很可能会遇到冲突。为了防止这种情况,只需在 XSD 设计器中将 AutoIncrementSeed
和 AutoIncrementStep
都设置为 -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
在 DataSet
的 Update
方法中,我们从 DataService 获取临时 DataSet
,ClientSideID
用于将原始行与更新后的行进行匹配。您可以在 OrderSet.vb 中找到此代码。
乐观并发通过向数据库中的每个表添加一个 Timestamp
字段来处理。已被其他用户更改的记录的更新将失败。如果您已将 DataAdapter
上的 ContinueUpdateOnError = True
设置为 True,则其他记录的更新将成功。在 update
操作之后,您可以检查由 DataTable.GetErrors
返回的行。
使用 DataSets 的想法
我在这里提出的框架解决了一些常见问题,例如将数据库字段与对象成员、标识字段、乐观并发进行映射。它还分离了对象和与数据库的通信。下一步可能是将包含 DataAdapter
的 Service
对象隐藏在 Web 服务中。我尝试过,效果非常好。
喜欢设计模式的人可以应用工厂模式来动态构建 DataSet
的 DataAdapter
。您还可以创建自己的 DataAdapterGenerator
或自己的 StoredProcedureGenerator
。在我看来,最好不要使用太多的代码生成器。您最终可能会在调整生成器上花费更多时间,而不是构建您为此付费的软件。
摘要
- 尽可能多地使用 Visual Studio 的内置设计器工具。
- 节省 O/R 映射工具——它们也不完美。
- 尽量限制必要的手动更改(例如,存储过程)的数量。
- 手动进行必要的更改,不要尝试重新发明轮子。
历史
- 2004 年 10 月 27 日:初始版本
许可证
本文没有明确的许可证,但可能在文章文本或下载文件中包含使用条款。如有疑问,请通过下面的讨论板联系作者。作者可能使用的许可证列表可在此处找到。