在 ADO .NET 中我们在合并两个相同或相近的 DataSet 对象时,通常会使用 DataSet 的 Merge 方法,该方法有多个重载版本,在介绍它之前我们先复习 Merge 方法,以下是 MSDN 中对 Merge 方法使用说明 :
Merge 方法用于合并架构大致相似的两个 DataSet 对象。合并在客户端应用程序上通常用于将数据源中最近的更改合并到现有的 DataSet 中。这使客户端应用程序能够拥有用数据源中的最新数据刷新的 DataSet 。通常在一系列过程的末尾调用 Merge 方法,这些过程涉及验证更改、消除错误、使用更改更新数据源并最后刷新现有的 DataSet 。
在客户端应用程序中,通常有这样一个按钮,用户可以单击它来收集已更改的数据并对其进行验证,然后将其发送回中间层组件。在这种情况下,将首先调用 GetChanges 方法。该方法返回另一个为验证和合并而优化的 DataSet 。第二个 DataSet 对象只包含已更改的 DataTable 和 DataRow 对象,结果产生初始 DataSet 的子集。该子集通常较小,因此可以更有效率地传递回中间层组件。然后,中间层组件将通过存储过程使用更改更新初始数据源。然后,中间层可以发送回一个新的 DataSet ,其中包含数据源中的初始数据和最新数据(通过再次运行初始查询);或者它可以发送回包含从数据源对其进行的所有更改的子集。(例如,如果数据源自动创建唯一主键值,则可以将这些值传播回客户端应用程序。)在哪一种情况下都可以使用 Merge 方法将返回的 DataSet 合并回客户端应用程序的初始 DataSet 。
当将新的源 DataSet 合并到目标中时, DataRowState 值为 Unchanged 、 Modified 或 Deleted 的任何源行都会与具有同一主键值的目标行相匹配。 DataRowState 值为 Added 的源行将匹配主键值与新源行相同的新目标行。
根据以上说明,我们知道 Merge 方法在合并两个数据集时,是以行的主键值为主要对比参照。这样在向数据集添加新行时不会有任何问题,在修改了行且不修改主键值的情况下也不会有问题,但是在更改行时如果你修改了主键的值,那问题就来了 …… 下面我们就举例说明:
在 SQL Server 下的一个 Products 表结构如下:
** 列名 ** ** **
|
** 数据类型 ** ** **
|
** 说明 ** ** **
---|---|---
** Code **
|
nchar
|
产品代码 ( 主键列 )
Name
|
nvarchar
|
产名名称
UnitPrice
|
numeric
|
产品单价
在 .NET 中使用 XSD 生成一个对应的 ProductsData.xsd 结构如下:
** 列名 ** ** **
|
** 数据类型 ** ** **
|
** 说明 ** ** **
---|---|---
** Code **
|
string
|
产品代码 ( 主键列 )
Name
|
string
|
产名名称
UnitPrice
|
decimal
|
产品单价
根据 MSDN 的说明,我们在前台通过 ProductsData 添加或修改数据后在提交后台更新时,通常做法如下:
// 创建一个新数据集来保存对主数据集所做的更改
ProductsData dataSetChanges;
dataSetChanges = (ProductsData)(productsData.GetChanges());
// 检查是否做了任何更改
if (dataSetChanges != null ) {
try {
// 需要做一些更改,所以尝试通过调用 update 方法
// 和传递数据集以及任何参数来更新数据源
UpdateDataSource(dataSetChanges);
** productsData.Merge(dataSetChanges); **
productsData.AcceptChanges();
}
catch (System.Exception eUpdate) {
throw eUpdate;
}
}
以上代码是根据 VS.NET 的数据窗体生成向导写的,依据以上代码我们模拟向数据集添加一行数据并更新后的情况:
** Code **
|
** Name **
|
** UnitPrice **
---|---|---
1001
|
金砂朱古力
|
120.00
没问题,下面我们修改这行数据再更新,这里我们把 Code 改为 1002 ,更新之后结果数据并没有按我们预想的把原本行 Code 列的值 1001 改为 1002 ,而是添加了一行:
** Code **
|
** Name **
|
** UnitPrice **
---|---|---
1001
|
金砂朱古力
|
120.00
1002
|
金砂朱古力
|
130.00
注:通常情况下我们是很少更改主键值,但在代码没有被使用的情况下,一般是允许更改 Code 的,特别是在系统实施的阶段。
出现以上问题的原因其实不奇怪,按 Merge 方法的原理,这一点也没错,但这是我们不希望的结果,怎么解决呢,难道不允许用户更改主键,但好象不符合实际。怎么解决呢?
既然要用 Merge 方法,那只有依 Merge 合并数据的原理去做,不让改主键我们就不改主键,我们可以给前台的 ProductsData 的数据集加多一个额外的主键 ID 列,并把它的 AutoIncrement 设为 true ,将原本的主键列 Code 改为 Key 列,至于后台 SQL Server 中的源表不作任何修改,修改后如下:
** 列名 ** ** **
|
** 数据类型 ** ** **
|
** 说明 ** ** **
---|---|---
** ID **
|
Int
|
自动增长列 (主键)
** Code **
|
String
|
产品代码 ( key 键)
Name
|
String
|
产名名称
UnitPrice
|
decimal
|
产品单价
经这样一改,由于 ID 是自动一个自递增列,我们并不去修改它的值,这样我们就可以随意更改 Code 列的值了, <SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: Arial; mso-bidi-font-fami