科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件DataSet中的数据并发性异常

DataSet中的数据并发性异常

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文调查了数据并发性异常背后的通常起因,介绍了解决这些问题的技术

作者:陶刚编译 来源:yesky 2007年11月9日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
第一个数据并发性异常

  该顾客服务应用程序使用了几个月没有任何问题,但是突然产生了一个没有处理的异常DBConcurrencyException。本段我将解释导致该异常的环境。

  第一个使用该应用程序的顾客销售服务人员Joe打开应用程序。这将初始化将数据载入数据集dsAllData并按每30分钟一次的周期来刷新数据。Joe的收件箱中有一堆文件,包括顾客传真、邮寄或者通过电子邮件发送的更改请求。他开始处理更改请求,但是经常被电话订单中断。

  其间,第二个客户销售服务人员Sally到达办公室并打开了她的应用程序实例。Sally的应用程序实例也初始化从SQL Server中载数据,包括Joe所作的更新。Sally也接到了一个顾客改变电话号码的请求。该顾客先前用电子邮件发送了地址的改变情况,但是在那时不知道他的新电话号码,现在要更新记录了。Sally使用Customer Maintenance屏幕更新顾客的电话号码。当Sally改变DataGrid中的电话号码时,新号码存储在dsAllData中。当Sally确认其它的顾客信息后,她意识到原地址的改变还没有处理,因此她更新那些信息并点击Save Changes按钮,将新数据发送到SQL Server数据库。

  Joe正在处理相同顾客的原始请求。当他打开Customer Maintenance屏幕时,应用程序从缓存数据集对象中读入信息。因为Sally更新顾客地址时,Joe的应用程序没有自动与数据库同步,因此他的Customer Maintenance屏幕仍然显示旧地址。Joe使用电子邮件提供的新信息改正了DataGrid中显示的信息,并点击Save Changes按钮。这样操作后出现了一个错误信息"并发性故障:更新命令影响了0个记录(Concurrency violation: the UpdateCommand affected 0 records)",应用程序崩溃了。在Joe再次打开应用程序时,他发现地址已经更新了,认为他的更改在应用程序崩溃前已经完成了。下面就是问题的代码行:

Private Sub butSave_Click (ByVal sender As System.Object, _
ByVal e As System.EventArgs)
 daCustomer.Update(dsAllData.Tables("Customer"))
End Sub

  实际的异常是DBConcurrencyException类型产生的,它是数据适配器对象内部建立的特定功能的结果(见图6)。该数据适配器设计为把数据填充到不连接的对象(例如数据集),这样它在执行更新前能自动地检查数据寻找改变。如果下层数据被改变了,数据适配器将引发一个DBConcurrencyException异常而不是执行更新。


图6. DBConcurrency异常

  执行完整性检查是相当直接的,它提高了数据表对象的性能,使它能够保持多个数据集合。当数据第一次载入数据表时,数据表中的所有数据行(DataRow)的DataRowVersion属性设置为原始的(Original)。当修改了数据行中的一列时,该行就被复制一份并标记为当前的(Current),标记为原始的行仍然没有改变。后来的所有对该数据的更改都仅仅影响当前行。当为一个数据表(或者一个数据集中的多个数据表)执行数据适配器的更新方法时,它重复所有的当前行来决定要发送给下层数据源的更新语句。作为DataRowVersion属性的补充,数据行有一个用于识别行中数据状态的RowState属性。它的可能值为Unchanged、Added、Modified、Deleted和Detached。

  在决定下层数据中的哪些行需要更新后,数据适配器dsCustomer建立更新SQL Server数据库所需要的SQL语句。在图1中我使用数据集和命令构造器对象来建立需要的INSERT、 UPDATE和DELETE语句。命令构造器对象建立的UPDATE语句使用DataRowVersion值为Original的数据行副本来识别和更新数据库中的适当行。这就是说,作为使用主键值简单地识别正确行的代替,命令构造器建立一个SQL语句来查找与数据集中存储的原始值准确匹配的行。下面的代码是建立的用于更新顾客电话号码的UPDATE语句示例:

UPDATE Customer SET Phone = @p1
WHERE ((ID = @p2) AND ((FirstName IS NULL AND @p3 IS NULL)
OR (FirstName = @p4))
AND ((LastName IS NULL AND @p5 IS NULL) OR (LastName = @p6))
AND ((Address IS NULL AND @p7 IS NULL) OR (Address = @p8))
AND ((Address2 IS NULL AND @p9 IS NULL) OR (Address2 = @p10))
AND ((City IS NULL AND @p11 IS NULL) OR (City = @p12))
AND ((State IS NULL AND @p13 IS NULL) OR (State = @p14))
AND ((Zip IS NULL AND @p15 IS NULL) OR (Zip = @p16))
AND ((Phone IS NULL AND @p17 IS NULL) OR (Phone = @p18)))

  该UPDATE语句使用参数而不是实际值,但是你能看到行中每列是怎样检查的。

  识别了准确的行和下层数据库中的原始值后,数据适配器就可以安全地更新行了。但是,如果自从数据表被填充后数据库中某行的某个列改变了,UPDATE语句将失败,因为数据库中没有与WHERE条件中的标准匹配的行了。数据适配器决定UPDATE是否成功很简单,只需要简单地检查数据库中被更新的行的实际数量。如果没有行被更新,那么下层数据一定被改变或删除了,就产生一个数据并发性异常。这就解释了Joe试图更新顾客电话号码时接收到的有点模糊的错误消息:数据适配器检查到的实际错误不是下层数据改变了,而是没有记录被更新,标志着下层数据必定被改变了。
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章