Linq to sql并发与事务
本文转载: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(); } |
Linq to sql并发与事务的更多相关文章
- Linq to Sql : 并发冲突及处理策略
原文:Linq to Sql : 并发冲突及处理策略 1. 通过覆盖数据库值解决并发冲突 try { db.SubmitChanges(ConflictMode.ContinueOnConflict) ...
- Linq to Sql并发冲突及处理策略
0. 并发冲突的示例 单用户的系统现在应该比较罕见了,一般系统都会有很多用户在同时进行操作:在多用户系统中,涉及到的一个普遍问题:当多个用户“同时”更新(修改或者删除)同一条记录时,该如何更新呢? ...
- LINQ to SQL语句(13)之开放式并发控制和事务
Simultaneous Changes开放式并发控制 下表介绍 LINQ to SQL 文档中涉及开放式并发的术语: 术语 说明 并发 两个或更多用户同时尝试更新同一数据库行的情形. 并发冲突 两个 ...
- 年终巨献 史上最全 ——LINQ to SQL语句
LINQ to SQL语句(1)之Where 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子句.Where操 ...
- LINQ TO SQL 大全
最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精 LINQ to SQL语句(1)之Where 适用场景: ...
- LINQ to SQL大全
LINQ to SQL语句 (1)之Where Where操作 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的 ...
- [转]LINQ To SQL 语法及实例大全
转载自:http://blog.csdn.net/pan_junbiao/article/details/7015633 LINQ to SQL语句(1)之Where Where操作 适用场景:实现过 ...
- LINQ to SQL语句非常详细(原文来自于网络)
LINQ to SQL语句(1)之Where Where操作 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子 ...
- LINQ To SQL 语法及实例大全
http://blog.csdn.net/pan_junbiao/article/details/7015633 http://blog.csdn.net/pan_junbiao/article/de ...
随机推荐
- java中的异常结构
1.基类为Throwable. 2.Error和Exception分别继承Throwable. 3.Error类异常描述了Java运行系统中的内部错误以及资源耗尽的情形.应用程序不应该抛出这种类型的对 ...
- C#遍历窗体控件(原文出自http://www.liangshunet.com/ca/201403/286434593.htm)
一.C#遍历窗体控件 主要遍历属于窗体(Form)的控件(Controls),假如窗体中有 Panel.Button 和 TextBox 控件,遍历代码如下: /// <summary> ...
- PHP不依赖系统自动执行机制
不依赖系统,以及不依赖yii事物机制,则考虑人为触发.触发可以写在总体的公共页面上,但是考虑到对数据库以及WWW服务器的压力问题,程序的延迟问题,需要对执行函数进行一些优化. 首先,我们考虑对数据库的 ...
- 面试小结(java基础)
一个.java源文件中可以有多个类吗?(内部类除外)有什么条件?带着这个疑惑,动手建几个测试类, 揭开心中的疑惑.以下是解开疑惑过程: package test;/** * 一个.java源文件中可以 ...
- CSS3 box-shadow(阴影使用)
from: http://jingyan.baidu.com/article/03b2f78c4d9fae5ea237aea6.html css3 box-shadow 内阴影与外阴影 1- box- ...
- .NET序列化的一点技巧
介绍 序列化是将对象状态转换为可保持或传输的形式的过程.序列化的补集是反序列化,后者将流转换为对象.这两个过程一起保证数据易于存储和传输. .NET Framework 提供了两个序列化技术: 二进制 ...
- Python Tutorial 学习(九)--Classes
## 9. Classes 类 Compared with other programming languages, Python's class mechanism adds classes wit ...
- linux c静态链接库与动态链接库
库函数是我们编程的时候经常用到的,我们协作编程的时候可以将常用的函数封装成库供大家使用,这样能够提高大家的工作效率.对于库函数,它分为动态链接库和静态链接库.对于静态链接库我们必须是连接到可执行文件中 ...
- smtp协议
一.smtp命令 1.HELO 向服务器标识用户身份 2.MAIL 初始化邮件传输mail from: <xxx> 3.RCPT 标识单个的邮件接收人:常在MAIL命令后面可有多个rcpt ...
- Yosemite重置Dock的命令
备忘 defaults write com.apple.dock ResetLaunchPad -bool true killall Dock