本文转载:http://www.cnblogs.com/lovecherry/archive/2007/08/20/862365.html

检测并发

首先使用下面的SQL语句查询数据库的产品表:

select * from products where categoryid=1

查询结果如下图:

为了看起来清晰,我已经事先把所有分类为1产品的价格和库存修改为相同值了。然后执行下面的程序:

var query = from p in ctx.Products where p.CategoryID == 1 select p;

foreach (var p in query)

p.UnitsInStock = Convert.ToInt16(p.UnitsInStock - 1);

ctx.SubmitChanges(); // 在这里设断点

我们使用调试方式启动,由于设置了断点,程序并没有进行更新操作。此时,我们在数据库中运行下面的语句:

update products

set unitsinstock = unitsinstock -2, unitprice= unitprice + 1

where categoryid = 1

然后在继续程序,会得到修改并发(乐观并发冲突)的异常,提示要修改的行不存在或者已经被改动。当客户端提交的修改对象自读取之后已经在数据库中发生改动,就产生了修改并发。解决并发的包括两步,一是查明哪些对象发生并发,二是解决并发。如果你仅仅是希望更新时不考虑并发的话可以关闭相关列的更新验证,这样在这些列上发生并发就不会出现异常:

[Column(Storage="_UnitsInStock", DbType="SmallInt", UpdateCheck = UpdateCheck.Never)]

[Column(Storage="_UnitPrice", DbType="Money", UpdateCheck = UpdateCheck.Never)]

为这两列标注不需要进行更新检测。假设现在产品价格和库存分别是27和32。那么,我们启动程序(设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是28和31。价格+1是之前更新的功劳,库存最终是-1是我们程序之后更新的功劳。当在同一个字段上(库存)发生并发冲突的时候,默认是最后的那次更新获胜。

 

解决并发

如果你希望自己处理并发的话可以把前面对列的定义修改先改回来,看下面的例子:

var query = from p in ctx.Products where p.CategoryID == 1 select p;

foreach (var p in query)

p.UnitsInStock = Convert.ToInt16(p.UnitsInStock - 1);

try

{

ctx.SubmitChanges(ConflictMode.ContinueOnConflict);

}

catch (ChangeConflictException)

{

foreach (ObjectChangeConflict cc in ctx.ChangeConflicts)

{

Product p = (Product)cc.Object;

Response.Write(p.ProductID + "<br/>");

cc.Resolve(RefreshMode.OverwriteCurrentValues); // 放弃当前更新,所有更新以原先更新为准

}

}

ctx.SubmitChanges();

首先可以看到,我们使用try{}catch{}来捕捉并发冲突的异常。在SubmitChanges的时候,我们选择了ConflictMode.ContinueOnConflict选项。也就是说遇到并发了还是继续。在catch{}中,我们从ChangeConflicts中获取了并发的对象,然后经过类型转化后输出了产品ID,然后选择的解决方案是RefreshMode.OverwriteCurrentValues。也就是说,放弃当前的更新,所有更新以原先更新为准。

我们来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是28和30。之前SQL语句库存-2生效了,而我们程序的更新(库存-1)被放弃了。在页面上也显示了所有分类为1的产品ID(因为我们之前的SQL语句是对所有分类为1的产品都进行修改的)。

然后,我们来修改一下解决并发的方式:

cc.Resolve(RefreshMode.KeepCurrentValues); // 放弃原先更新,所有更新以当前更新为准

来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是27和31。产品价格没有变化,库存-1了,都是我们程序的功劳,SQL语句的更新被放弃了。

然后,我们再来修改一下解决并发的方式:

cc.Resolve(RefreshMode.KeepChanges); // 原先更新有效,冲突字段以当前更新为准

来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是28和31。这就是默认方式,在保持原先更新的基础上,对于发生冲突的字段以最后更新为准。

我们甚至还可以针对不同的字段进行不同的处理策略:

foreach (ObjectChangeConflict cc in ctx.ChangeConflicts)

{

Product p = (Product)cc.Object;

foreach (MemberChangeConflict mc in cc.MemberConflicts)

{

string currVal = mc.CurrentValue.ToString();

string origVal = mc.OriginalValue.ToString();

string databaseVal = mc.DatabaseValue.ToString();

MemberInfo mi = mc.Member;

string memberName = mi.Name;

Response.Write(p.ProductID + " " + mi.Name + " " + currVal + " " + origVal +" "+ databaseVal + "<br/>");

if (memberName == "UnitsInStock")

mc.Resolve(RefreshMode.KeepCurrentValues); // 放弃原先更新,所有更新以当前更新为准

else if (memberName == "UnitPrice")

mc.Resolve(RefreshMode.OverwriteCurrentValues); // 放弃当前更新,所有更新以原先更新为准

else

mc.Resolve(RefreshMode.KeepChanges); // 原先更新有效,冲突字段以当前更新为准

}

}

比如上述代码就对库存字段作放弃原先更新处理,对价格字段作放弃当前更新处理。我们来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别为28和31了。说明对价格的处理确实保留了原先的更新,对库存的处理保留了当前的更新。页面上显示的结果如下图:

最后,我们把提交语句修改为:

ctx.SubmitChanges(ConflictMode.FailOnFirstConflict);

表示第一次发生冲突的时候就不再继续了,然后并且去除最后的ctx.SubmitChanges();语句。来测试一下,在执行了SQL后再继续程序可以发现界面上只输出了数字1,说明在第一条记录失败后,后续的并发冲突就不再处理了。

 

事务处理

Linq to sql在提交更新的时候默认会创建事务,一部分修改发生错误的话其它修改也不会生效:

ctx.Customers.Add(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });

ctx.Customers.Add(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });

ctx.SubmitChanges();

假设数据库中已经存在顾客ID为“abcde”的记录,那么第二次插入操作失败将会导致第一次的插入操作失效。执行程序后会得到一个异常,查询数据库发现“abcdf”这个顾客也没有插入到数据库中。

如果每次更新后直接提交修改,那么我们可以使用下面的方式做事务:

if (ctx.Connection != null) ctx.Connection.Open();

DbTransaction tran = ctx.Connection.BeginTransaction();

ctx.Transaction = tran;

try

{

CreateCustomer(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });

CreateCustomer(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });

tran.Commit();

}

catch

{

tran.Rollback();

}

private void CreateCustomer(Customer c)

{

ctx.Customers.Add(c);

ctx.SubmitChanges();

}

运行程序后发现增加顾客abcdf的操作并没有成功。或者,我们还可以通过TransactionScope实现事务:

using (TransactionScope scope = new TransactionScope())

{

CreateCustomer(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });

CreateCustomer(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });

scope.Complete();

}

转载于:https://www.cnblogs.com/51net/p/3552613.html

Linq to sql并发与事务相关推荐

  1. Linq to SQL之使用事务

    事务是一个原子的工作单位,必须完整的完成单位里的所有工作,要么全部执行,要么全部都不执行.如果提交事务,则事务执行成功:如果回滚事务,则事务执行失败. 事务具备4个基本特性--ACID(原子性.一致性 ...

  2. LINQ To SQL 语法及实例大全

    LINQ to SQL语句(1)之Where Where操作 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子 ...

  3. LINQ to SQL语句(1)之Where(抄的好)

    Where操作适用场景:实现过滤,查询等功能.说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子句.Where操作包括3种形式,分别为简单形式.关 ...

  4. LINQ to SQL语句 收藏系列

    KB-Transaction in Linq to SQL  http://blog.darkthread.net/post-2008-05-14-transaction-in-linq-to-sql ...

  5. C# LINQ TO SQL

    LINQ to SQL语句(1)之Where   Where操作   适用场景:实现过滤,查询等功能.   说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它 ...

  6. Linq to Sql 语句全集

    LINQ to SQL语句(1)之Where Where操作 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子 ...

  7. Linq to SQL 资源

    Scott Guthrie 的 Linq to SQL 系列: 1)介绍 http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to ...

  8. Linq To Sql 练习

    使用LINQ to SQL (第一部分) 2009-05-12 14:33:21|  分类: 默认分类 |  标签: |字号大中小 订阅 什么是LINQ to SQL? LINQ to SQL 是随. ...

  9. 一步一步学Linq to sql(七):并发与事务

      检测并发 首先使用下面的SQL语句查询数据库的产品表: select * from products where categoryid=1 查询结果如下图: 为了看起来清晰,我已经事先把所有分类为 ...

最新文章

  1. flex和bison实例分析
  2. python文本编码转换_Python: 转换文本编码
  3. android 原始编译过程,Android编译系统环境初始化过程分析.doc
  4. Halcon:手眼标定——眼在手外与眼在手上
  5. 创建一个最简单的imgui测试用例
  6. 使用HISTCONTROL强制history忽略某条特定命令
  7. 互联网架构设计漫谈 (2)
  8. windows下监测tomcat7内存使用情况
  9. 《iOS9开发快速入门》——第2章,第2.1节Xcode 7.0的新特性
  10. python的功能及特点_使用Python这么多年,才发现Python还有这些实用的功能和特点...
  11. java创建内部面板类_Java图形与文本(18)
  12. docker容器运行jar
  13. AI智能电话机器人源码搭建揭秘!语音机器人电销机器人源码系统那些事
  14. PS-如何用ps软件看psd文件中的字体大小
  15. subli快速度创建html,JS插件——自定义下拉框
  16. fences卸载_Win10系统怎样卸载fences?Win10系统卸载fences图文教程-系统城
  17. html屏蔽浏览器自动填充,禁止浏览器对表单自动填充的几种方法
  18. 英语语法---名词性从句详解
  19. 开放PLM——Aras Innovator编程学习(一)简介
  20. 在线选毕业照片相册系统开发

热门文章

  1. Kafka常用命令之kafka-console-consumer.sh
  2. MySQL无法读表错误的解决方法(MySQL 1018 error)
  3. Flask 区域块简单原理实现
  4. DelphiMVC连接池配置
  5. [Head First设计模式]生活中学设计模式——组合模式
  6. HDU 3441 Rotation
  7. MS CRM 2011 汇总更新4已经发布
  8. 初学者学习C++的50条忠告
  9. 深入理解NIO - Selector、ServerSocketChannel、SocketChannel底层原理
  10. catia需要java插件,catia中的带分析