在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 列的值了,merge 方法在合并数据时由于是依据id例进行比对所以也不会再出现前面加多一行的问题了。
后记:这是我写的 blog 申请好之后写的第一篇有关技术的文章,自已感觉写的有点罗嗦,其实写那么长有用的只是最后的几行,这可能对那些.net高手们只是雕虫小技,所以让高手们见笑了。