数据源生成主键值之处理方法

研究了一下午,原来IssueVision并没有解决这个问题,既然数据记录的修改是依据主键的,看来只能用比较原始的方法绕过去....不爽

None.gif 11.5  分离和重新集中改变
None.gif
11.5 . 1  用GetChanges方法节省带宽
None.gifDataSet和DataTable对象都公开了一个重载的GetChanges方法。在调用DataSet对象的GetChanges方法时,收到一个新的DataSet对象,它具有与原来的对象一样的结构,但是只包含原来DataSet中已修改的行。
None.gif
None.gif注意:严格来说上面的说法并不准确,除了包含请求的已修改行外,它可能还包含其他维持引用完整性所需要的行。
None.gif
None.gif在前面我们提到了获取时间戳的开放式并发。我们获取新的值并存储在web服务的DataSet中。但是DataSet与客户端应用程序是分离的。怎样将新的时间戳加入到客户端应用程序的DataSet中呢?
None.gif
None.gif当然,我们可以返回一个包含与客户端应用程序所有数据相同的新DataSet,但这显然不是利用带宽的最好方式。
None.gif
None.gif最经济的办法是让web服务返回它所收到的DataSet,并包括新的时间戳值。
None.gif
None.gif
None.gif但这只是解决了问题的一部分。客户端现在有了新的时间戳值,但是如何将web服务返回的DataSet集成到客户端应用程序的DataSet中去呢?
None.gif
None.gif
11.5 . 1.1  DataSet对象的Merge方法。
None.gif
None.gif简单的答案是使用DataSet对象的Merge方法。DataSet对象的Merge方法让您可以将DataSet,DataTable,DataRow对象数组的内容合并到一个现有的DataSet中。
None.gif
None.gif
None.gif不过,这个例子并不有用。一些开发人员会将包含DataTable对象的、具有相同名称但是有完全不同结构的两个DataSet对象结合在一起。
None.gif
None.gif
None.gif注意两个例子不同之处在于主键。如果ADO.NET在合并数据时遇到有同样主键的行,会把内容合并到一行中。注意图中有错误,第二个DataTable最后一个ID应该是4。
None.gif
None.gif注意在Merge方法的结果中,来自被合并的DataSet的数据具有优先权。
None.gif
None.gif所以我们的解决方案是:
None.gif
None.gif
None.gif代码:
None.gif
None.gifWebServiceClass objWebService 
=   new  WebServiceClass();
None.gif
None.gifDataSet dsMain 
=  objWebService.GetDataSet();
None.gif
None.gifModifyDataSetContents(dsMain);
None.gif
None.gifDataSet dsChanges 
=  dsMain.GetChanges();
None.gif
None.gifdsChanges 
=  objWebService.SubmitChanges(dsChanges);
None.gif
None.gifdsMain.Merge(dsChanges);
None.gif
None.gif
11.5 . 1.2  Merge方法和RowState属性
None.gif
None.gif我们几乎已经完成了,但是还差一点。如果我们检查原来在主DataSet中修改的内容,那么您就会发现他们确实有了新的时间戳值。不过这些行的RowState属性仍然是Modified。
None.gif
None.gif如果用户再次提交更改,那么GetChanges方法返回的DataSet仍然包括用户以前修改的行。当web服务收到这个DataSet并试图提交更改时,更新尝试会引发一个异常,因为数据库已经包含了这些更改。
None.gif
None.gif因为ado.net并不知道我们已经向数据库提交了更改。当web服务提交改变时,ado.net将已修改的行的RowState从Modified改为Unmodified。但这个改变发生在web服务的DataSet上。ADO.NET不改变客户端应用程序的主DataSet的已修改行的RowState,因为这两个DataSet间并没有链接。
None.gif
None.gif合并DataSet不会改变主DataSet的修改行的RowState属性。我们可以在调用Merge后,对主DataSet调用AcceptChanges方法将已修改行的RowState改回为Unmodified,如下:
None.gif
None.gifWebServiceClass objWebService 
=   new  WebServiceClass();
None.gif
None.gifDataSet dsMain 
=  objWebService.GetDataSet();
None.gif
None.gifModifyDataSetContents(dsMain);
None.gif
None.gifDataSet dsChanges 
=  dsMain.GetChanges();
None.gif
None.gifdsChanges 
=  objWebService.SubmitChanges(dsChanges);
None.gif
None.gifdsMain.Merge(dsChanges);
None.gif
None.gifdsMain.AcceptChanges();
None.gif
None.gif
11.5 . 1.3  Merge方法和自动递增值
None.gif
None.gif在提交新增的定单后,新产生的OrderID包含在web服务返回的DataSet中,不过这些值无法与原值合并,因为主键不一样。
None.gif
None.gif
None.gif我们有下面几种解决方案:
None.gif
None.gifü         合并前清理
None.gif
None.gif在合并前从主DataSet中删除新定单达到所需要的结果:
None.gif
None.gifWebServiceClass objWebService 
=   new  WebServiceClass();
None.gif
None.gifDataSet dsMain 
=  objWebService.GetDataSet();
None.gif
None.gifModifyDataSetContents(dsMain);
None.gif
None.gifDataSet dsChanges 
=  dsMain.GetChanges();
None.gif
None.gifdsChanges 
=  objWebService.SubmitChanges(dsChanges);
None.gif
None.gif 
None.gif
None.gif
// Remove the pending new orders from the main DataSet
None.gif
None.gif
// before merging the orders returned by the Web service.
None.gif

None.gifDataTable tbl 
=  dsMain.Tables[ " Orders " ];
None.gif
None.gif
foreach (DataRow row  in  tbl.Select( "" "" , DataRowViewState.Added))
None.gif
None.gif    tbl.Rows.Remove(row);
None.gif
None.gifdsMain.Merge(dsChanges);
None.gif
None.gifdsMain.AcceptChanges();
None.gif
None.gifü         改变DataSet对象中的主键
None.gif
None.gif在原来的DataTable中增加一个新列,起名为PseudoKey。
None.gif
None.gif
None.gif如何在合并前改变主键并在其后将主键重新设置回去呢?代码:
None.gif
None.gifWebServiceClass objWebService 
=   new  WebServiceClass();
None.gif
None.gifDataSet dsMain 
=  objWebService.GetDataSet();
None.gif
None.gifModifyDataSetContents(dsMain);
None.gif
None.gifDataSet dsChanges 
=  dsMain.GetChanges();
None.gif
None.gifdsChanges 
=  objWebService.SubmitChanges(dsChanges);
None.gif
None.gif 
None.gif
None.gif
// Change the primary key in each table to be the pseudokey.
None.gif

None.gifDataTable tblMain 
=  ds.Tables[ " Orders " ];
None.gif
None.gifDataColumn[] pkOriginal 
=  tblMain.PrimaryKey;
None.gif
ExpandedBlockStart.gifContractedBlock.giftblMain.PrimaryKey 
=   new  DataColumn[]  dot.gif {tblMain.Columns["PseudoKey"]} ;
None.gif
None.gifDataTable tblChanges 
=  dsChanges.Tables[ " Orders " ];
None.gif
ExpandedBlockStart.gifContractedBlock.giftblChanges.PrimaryKey 
=   new  DataColumn[]  dot.gif {tblChanges.Columns["PseudoKey"]} ;
None.gif
None.gif 
None.gif
None.gifdsMain.Merge(dsChanges);
None.gif
None.gif 
None.gif
None.gif
// Set the primary key in the main table back to its original value.
None.gif

None.giftblMain.PrimaryKey 
=  pkOriginal;
None.gif
None.gif 
None.gif
None.gifdsMain.AcceptChanges();
None.gif
None.gif但是PseudoKey列的值应该怎样取呢?如果它不对应于数据库中的一列,如何产生一个唯一值呢?我们可以使用另一个递增列。
None.gif
None.gif
11.5 . 1.3  可用的选项小结
None.gif
None.gif略。
None.gif
None.gif
11.6  得体地处理失败的更新尝试
None.gif
11.6 . 1  事先为冲突做出计划
None.gifDataAdapter对象的ContinueUpdateOnError属性
None.gif
None.gif
11.6 . 2  通知用户失败
None.gif代码1:
None.gif
None.gif
try
None.gif
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
InBlock.gif    MyDataAdapter.ContinueUpdateOnError 
= true;
InBlock.gif
InBlock.gif    MyDataAdapter.Update(MyDataTable);
InBlock.gif
InBlock.gif    
if (MyDataTable.HasErrors)
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif
InBlock.gif        
string strMessage;
InBlock.gif
InBlock.gif        strMessage 
= "The following row(s) were not updated " +
InBlock.gif
InBlock.gif                     
"successfully:";
InBlock.gif
InBlock.gif        
foreach (DataRow row in MyDataTable.Rows)
InBlock.gif
InBlock.gif            
if (row.HasErrors)
InBlock.gif
InBlock.gif                strMessage 
+= "\n\r" + (string) row["ID"+ 
InBlock.gif
InBlock.gif                              row.RowError;
InBlock.gif
InBlock.gif        MessageBox.Show(strMessage);
InBlock.gif
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
else
InBlock.gif
InBlock.gif        MessageBox.Show(
"All updates succeeded");
InBlock.gif
ExpandedBlockEnd.gif}

None.gif
None.gif
catch  (Exception ex)
None.gif
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
InBlock.gif    MessageBox.Show(
"The following exception occurred: \n\r" + 
InBlock.gif
InBlock.gif                    ex.Message);
InBlock.gif
ExpandedBlockEnd.gif}

None.gif
None.gif我们可以为用户提交这样的信息:(图略)
None.gif
None.gif
None.gif代码:
None.gif
None.gifDataTable tbl 
=  CreateFillAndModifyTable();
None.gif
None.gifDataRow row 
=  tbl.Rows[ 0 ];
None.gif
None.gifConsole.WriteLine(
" Current  Balance Due:  "   +  row[ " BalanceDue " ]);
None.gif
None.gifConsole.WriteLine(
" Original Balance Due:  "   +  
None.gif
None.gif                  row[
" BalanceDue " , DataRowVersion.Original]);
None.gif
None.gif我们已经知道如何用DataRow访问一行的当前和原来内容,但我们如何从数据库中提取当前内容呢?
None.gif
None.gif
11.6 . 3  提取冲突行的当前内容
None.gif要提取所需行的当前内容,我们可以使用DataAdapter对象的RowUpdated事件。下面代码确定在更新尝试中DataAdapter是否遇到错误。如果错误是一个并发异常,那么代码会用带参数的查询提取数据库中相应行的内容。
None.gif
None.gif代码:
None.gif
None.gif
private   void  HandleRowUpdated( object  sender, OleDbRowUpdatedEventArgs e)
None.gif
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
InBlock.gif    
if ((e.Status == UpdateStatus.ErrorsOccurred) &&
InBlock.gif
InBlock.gif        (e.Errors.GetType 
== typeof(DBConcurrencyException))
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif
InBlock.gif        ConflictAdapter.SelectCommand.Parameters[
0].Value = e.Row["ID"];
InBlock.gif
InBlock.gif        
int intRowsReturned = ConflictAdapter.Fill(ConflictDataSet);
InBlock.gif
InBlock.gif        
if (intRowsReturned == 1)
InBlock.gif
InBlock.gif            e.Row.RowError 
= "The row has been modified by another user.";
InBlock.gif
InBlock.gif        
else
InBlock.gif
InBlock.gif            e.Row.RowError 
= "The row no longer exists in the database.";
InBlock.gif
InBlock.gif        e.Status 
= UpdateStatus.Continue;
InBlock.gif
ExpandedSubBlockEnd.gif    }

InBlock.gif
ExpandedBlockEnd.gif}

None.gif
None.gif注意:如果没有将Status改变为Continue或SkipCurrentRow,那么在更新尝试失败时DataAdapter会自动将文字附加到RowError属性上。
None.gif
None.gif这个代码段提取数据库中相应行的当前内容并加入到单独的DataSet中,这样您就可以在更新尝试之后对数据进行检查。现在您有了构建上图信息的所有数据。
None.gif
None.gif
11.6 . 4  如果第一次没有成功
None.gif用户可能不希望重新查询数据库并重新对新数据进行同样的改变才能重新提交这些改变。我们如何使用ado.net模型简化这一过程?
None.gif
None.gif失败的原因在于DataAdapter在并发检查中使用的数据与数据库中这行的当前内容不匹配。在刷新DataRow的内容前,欧文们不能向数据库存储在DataRow中的改变。
None.gif
None.gif只要我们可以改变DataRow的original 值,就可以顺利提交改变。
None.gif
None.gif使用DataSet对象的Merge方法导入“New Original”值
None.gif
None.gif在前面代码片段中,我们捕获了DataAdapter对象的RowUpdated事件。如果当前行没有成功更新,那么代码就捕获数据库中相应行的内容加入到ConflictDataSet的新DataSet中。假设主DataSet称为MainDataSet,我们可以用下面一行代码将ConflictDataSet的内容合并到MainDataSet中:
None.gif
None.gifMainDataSet.Merge(ConflictDataSet, 
true );
None.gif
None.gif这段代码不改变主DataSet中的DataRow对象的当前内容。它只是用冲突的DataSet的数据覆盖DataRow对象的原来值。
None.gif
None.gif于是便可以更新了。
None.gif
None.gif注意:不能得到有关在数据库中不再存在的行的“New Original”信息。如果一个更新尝试因为行在数据库中不再存在而失败,那么就不能用这种方法刷新行中的原来值。如果希望向数据库重新添加DataRow对象的当前内容,那么可以用从DataTable对象的Rows集合中删除这个DataRow再重新添加它。这样会使DataRow对象的RowState改变为Added。
None.gif
None.gif

转载于:https://www.cnblogs.com/skinia/archive/2005/09/06/231437.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值