Larry Yang

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

5.          事务以及并发问题:

5.1 开放式并发概述 (LINQ to SQL)

LINQ to SQL 支持开放式并发控制。所谓开放式并发控制,是指先调查其他事务是否已更改了行中的值,再允许提交更改的技术。相比之下,保守式并发控制则是通过锁定记录来避免发生并发冲突。之所以称作开放式控制,是因为它将一个事务干扰另一事务视为不太可能发生。

 

冲突解决:通过重新查询数据库刷新出现冲突的项,然后协调差异的过程。刷新对象时,LINQ to SQL 更改跟踪器会保留以下数据:

*       最初从数据库获取并用于更新检查的值。

*       通过后续查询获得的新数据库值。

LINQ to SQL 随后会确定相应对象是否发生冲突(即它的一个或多个成员值是否已发生更改)。如果此对象发生冲突,LINQ to SQL 下一步会确定它的哪些成员发生冲突。

LINQ to SQL 发现的任何成员冲突都会添加到冲突列表中。

 

在 LINQ to SQL 对象模型中,当以下两个条件都得到满足时,就会发生“开放式并发冲突”:

*        客户端尝试向数据库提交更改。

*       数据库中的一个或多个更新检查值自客户端上次读取它们以来已得到更新。

此冲突的解决过程包括查明对象的哪些成员发生冲突,然后决定您希望如何进行处理。

 

5.2 事务 (LINQ to SQL)

LINQ to SQL 支持三种不同的事务模型。下文按执行检查的顺序列出了这些模型。(引用自MSDN)

5.2.1 显式本地事务

调用 SubmitChanges 时,如果 Transaction 属性设置为 (IDbTransaction) 事务,则在同一事务的上下文中执行 SubmitChanges 调用。

成功执行事务后,要由您来提交或回滚事务。与事务对应的连接必须与用于构造DataContext 的连接匹配。如果使用其他连接,则会引发异常。

5.2.2 显式可分发事务

可以在当前 Transaction 的作用域中调用 LINQ to SQL API(包括但不限于 SubmitChanges)。LINQ to SQL 检测到调用是在事务的作用域内,因而不会创建新的事务。在这种情况下,<token>vbtecdlinq</token>还会避免关闭连接。您可以在此类事务的上下文中执行查询和 SubmitChanges 操作。

5.2.3 隐式事务

当您调用 SubmitChanges 时,LINQ to SQL 会检查此调用是否在 Transaction 的作用域内或者 Transaction 属性 (IDbTransaction) 是否设置为由用户启动的本地事务。如果这两个事务它均未找到,则 LINQto SQL 启动本地事务(IDbTransaction),并使用此事务执行所生成的SQL 命令。当所有 SQL 命令均已成功执行完毕时,LINQ to SQL 提交本地事务并返回。

5.2.4事务代码例子

Implicit(隐式)例子

说明:这个例子在执行SubmitChanges()操作时,隐式地使用了事务。因为在更新2种产品的库存数量时,第二个产品库存数量为负数了。这导致了更新产品全部失败了,系统回滚到这个操作的初始状态。

try {

   Product prod1 = db.Products.First(p => p.ProductID == 4);

   Product prod2 = db.Products.First(p => p.ProductID == 5);

   prod1.UnitsInStock -= 3;

    prod2.UnitsInStock-= 5;//错误:库存数量的单位不能是负数

    //要么全部成功要么全部失败

    db.SubmitChanges();

}

catch (System.Data.SqlClient.SqlException e) {

//执行异常处理

}

Explicit(显式)例子

说明:这个例子使用事务封闭数据提交。

using (TransactionScope ts = newTransactionScope()) {

    try{

       Product prod1 = db.Products.First(p => p.ProductID == 4);

       Product prod2 = db.Products.First(p => p.ProductID == 5);

       prod1.UnitsInStock -= 3;

       prod2.UnitsInStock -= 5;//错误:库存数量的单位不能是负数

       db.SubmitChanges();

    }

   catch (System.Data.SqlClient.SqlException e) {

       //执行异常处理

    }

}

5.3如何:管理更改冲突 (LINQ to SQL)

5.3.1检索成员冲突信息

基本方法:

Linq to Sql检测在更新时是否产生了冲突的基本方法:将该记录每个字段原来的值和更新时的值进行对比,如果稍有不同则意味着记录被修改过,因此产生了更新冲突。

缺点: 如果一个表中有数十个字段,那么更新就必须完整地检测一遍。再者,如果其中某一个字段储存了洋洋洒洒上万字的文章,那么在验证时仅仅是将它从Web服务器发送到数据库服务器就需要耗费可观的带宽与时间。

Linq to Sql提供了另外一种检测并发更新冲突的方式:使用记录的时间戳。通过设置使timestamp类型的数据在记录被修改时就会设置,因此在更新时其他纪录的值与之前相同,也会引发更新冲突。

详细代码用例可参考:

http://kb.cnblogs.com/page/42512/?page=1

http://kb.cnblogs.com/page/42513/?page=1

5.3.2解决并发冲突

Linq to Sql检测在更新时是否产生了冲突的基本方法:将该记录每个字段原来的值和更新时的值进行对比,如果稍有不同则意味着记录被修改过,因此产生了更新冲突。

解决冲突的方法有三种:

1) 通过保留数据库值解决并发冲突。

2) 通过覆盖数据库值保留当前值。

3) 通过将数据库值与当前值合并来解决冲突。

具体详情可参见msdn: http://msdn.microsoft.com/zh-cn/library/bb399389.aspx

posted on 2010-03-02 10:38  Larry Yang  阅读(385)  评论(0)    收藏  举报