Entity Framework 乐观并发控制

一、背景

  我们知道,为了防止并发而出现脏读脏写的情况,可以使用Lock语句关键字,这属于悲观并发控制的一种技术,,但在分布式站点下,锁的作用几乎不存在,因为虽然锁住了A服务器的实例对象,但B服务器上的锁是不知道的A服务器上锁的情况的,所以,面对分布式站点、单一数据库这种架构,我们可以使用EntityFramework的乐观并发控制来解决这个问题,EF对并发控制有不管控和乐观并发控制两种,默认情况是不管控,但EF不支持悲观并发。

lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。

二、悲观并发和乐观并发

悲观并发:比如有两个用户A,B,同时登录系统修改一个文档,如果A先进入修改,则系统会把该文档锁住,B就没办法打开了,只有等A修改完,完全退出的时候B才能进入修改。

乐观并发:同上面的例子,A,B两个用户同时登录,如果A先进入修改紧跟着B也进入了。A修改文档的同时B也在修改。如果在A保存之后B再保存他的修改,此时系统检测到数据库中文档记录与B刚进入时不一致,B保存时会抛出异常,修改失败。

乐观并发的基本出发点是:当保存数据的时候抱着一种乐观的态度,不期望发生并发冲突,即使万一发生并发冲突,也能捕捉到冲突异常,然后根据策略解决冲突,而解决冲突的方式一般分为Client wins(以后操作者为赢) 和 Store wins(以先存储的数据为赢)。

三、EF中如何控制并发

第1步、

  在设计器中,对需要进行并发控制的字段的ConcurrencyMode并发模式设置为Fixed,这是检测是否发生冲突的指标,该字段最终会在EF生成SQL时的where子句出现,如果没有设置为Fixed,即使该字段出现并发冲突,EF也不会报出并发异常,从而会导致出现脏读脏写的情况。

第2步、

 Resolving optimistic concurrency exceptions with Reload

  使用Reload数据作为解决乐观并发异常的策略之一,我在这里就讲数据Reload这一种策略就好了,除了Reload外,还有其他几种冲突解决策略,详见参考文献中EF官方团队博客。

  微软Entity Framework 团队官方博客 推荐处理乐观并发冲突的策略之一是Reload数据,也就是EF检测到并发冲突时会抛出DbUpdateConcurrencyException,这时解决冲突分为Client Wins或者Store Wins ,而Reload处理也就是Store Wins,意味着放弃当前内存中的实体,重新到数据库中加载当前实体,EF官方团队给出来的示例代码如下,其他几种策略请见参考文献链接。

using (var context = new UnicornsContext())
{
    bool saveFailed;
    do
    {
        saveFailed = false;
        var unicorn = context.Unicorns.Find(1);
        unicorn.Name = "tom";
        try
        {
            context.SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            saveFailed = true;
            // Update the values of the entity that failed to save
            // from the store
            ex.Entries.Single().Reload();
        }
    } while (saveFailed);
}

四、SqlProfiler看原理

  完成以上步骤后,您的程序就具备了乐观并发处理的能力了,但是,其中的原理是什么呢?从SqlProfiler监控结果来看,EF是将并发模式设置为Fixed的字段放在where子句里,后操作会因为取不到相应的记录而更新失败,打个比方说,当两个用户同时对同一条记录(ID=1,Count=10)进行读、写操作的时候,A、B同时读取记录的时候,Count都等于10,A用户先进行Update减1操作,此时(ID=1,Count=9),而此时B用户稍微晚一点点再进行Update操作的时候,因为Count已经被A修改成9了,已经不存在(ID=1,Count=10)的记录了,所以B最终执行影响行数为0,EntityFramework抛出并发异常:

如:我们给Count属性的并发模式设置成Fixed的话,那生成的SQL语句如下:



exec sp_executesql N‘update [dbo].[OrderLog]
set [Count] = @0
where (([ID] = @1) and ([Count] = @2))‘,N‘@0 int,@1 int,@2 int‘,@0=78,@1=1,@2=9 

当EntityFramework执行更新操如果影响行数为0,就会抛出异常,相关源代码如下所示:

五、总结

   个人认为,乐观并发控制适用于并发量还不是很大情况,也就是符合乐观并发的初衷,当保存数据的时候不期望发生并发冲突,一旦发生冲突,也能捕捉到冲突异常,然后根据策略解决冲突或者提示用户操作失败等,但是,当并发量很大的时候,我认为乐观并发就显得并不那么适用了,个人建议,做评估项目并发风险和做并发冲突的测试,假如完全可以接受,大可以应用EntityFramework的乐观并发控制,实现起来也比较简单,假如项目并发量确实很大,那可以考虑别的技术方案实现,比如消息队列……等。

参考文献

(1)微软EntityFramework团队博客: Using DbContext in EF 4.1 Part 9: Optimistic Concurrency Patterns

(2)Gyoung: Entity Framework 并发处理

Entity Framework 乐观并发控制

时间: 2024-11-02 23:35:44

Entity Framework 乐观并发控制的相关文章

Entity Framework 并发处理详解

引言 在软件开发过程中,并发控制是确保及时纠正由并发操作导致的错误的一种机制.从 ADO.NET 到 LINQ to SQL 再到如今的 ADO.NET Entity Framework,.NET 都为并发控制提供好良好的支持方案.并发处理方式一般分为乐观必并发与悲观必并发两种,本文将为大家介绍 Entity Framework . LINQ to SQL 中的并发处理方式.在本文最后,将提供一个了可参考的方案,结合事务 Transaction 处理复杂性对象的并发. 目录 一.并发处理的定义

C#综合揭秘——Entity Framework 并发处理详解

引言 在软件开发过程中,并发控制是确保及时纠正由并发操作导致的错误的一种机制.从 ADO.NET 到 LINQ to SQL 再到如今的 ADO.NET Entity Framework,.NET 都为并发控制提供好良好的支持方案. 并发处理方式一般分为乐观必并发与悲观必并发两种,本文将为大家介绍 Entity Framework . LINQ to SQL 中的并发处理方式.在本文最后,将提供一个了可参考的方案,结合事务 Transaction 处理复杂性对象的并发. 目录 一.并发处理的定义

Entity Framework 6 Code First 系列:无需修改实体和配置-在MySql中使用和SqlServer一致的并发控制

无需修改实体和配置,在MySql中使用和SqlServer一致的并发控制.修改RowVersion类型不可取,修改为Timestamp更不可行.Sql Server的RowVersion生成一串唯一的二进制保证Row的版本,无关TimeStamp,更无论TimeStamp的精度问题.使用MySql触发器只能解决uuid的插入的默认值和更新的随机值,由于MySql的自身为了防止无限递归的策略,它的触发器无法在当前表的触发器中更新当前表,所以触发器无法实现更新在SqlServer中由数据库生成的Ro

Entity Framework 并发处理

什么是并发? 并发分悲观并发和乐观并发. 悲观并发:比如有两个用户A,B,同时登录系统修改一个文档,如果A先进入修改,则系统会把该文档锁住,B就没办法打开了,只有等A修改完,完全退出的时候B才能进入修改. 乐观并发:同上面的例子,A,B两个用户同时登录,如果A先进入修改紧跟着B也进入了.A修改文档的同时B也在修改.如果在A保存之后B再保存他的修改,此时系统检测到数据库中文档记录与B刚进入时不一致,B保存时会抛出异常,修改失败. EF中如何控制并发? Entity Framework不支持悲观并发

Entity Framework 学习

Entity Framework 学习初级篇1--EF基本概况... 2 Entity Framework 学习初级篇2--ObjectContext.ObjectQuery.ObjectStateEntry.ObjectStateManager类的介绍... 7 Entity Framework 学习初级篇3-- LINQ TOEntities. 10 Entity Framework 学习初级篇4--EntitySQL. 17 Entity Framework 学习初级篇5--ObjectQ

Entity Framework教程(第二版)

源起 很多年前刚毕业那阵写过一篇关于Entity Framework的文章,没发首页却得到100+的推荐.可能是当时Entity Framework刚刚发布介绍EF的文章比较少.一晃这么多年过去了,EF6.1已经发布很久,EF7马上就到来.那篇文章已经显得相当过时,这期间园子里出现了很多介绍EF4/5/6版本的精彩文章,我的工作中也没有在持续使用EF,本来也就不准备再写现在这篇文章了.后来看到之前那篇文章还是有很多朋友在评论里给予鼓励,再加上自己确实在使用新版EF的过程中也总结了一些心得,解决了

Entity Framework之问题收集

本节讨论内容主要针对收集了上篇文章大家碰到问题的讨论解决,会持续收集扩充. DbContext加载原值,当前值,数据库值,属性操作,对象复制,对象值复制(VO,DTO->POCO),复杂对象取值 DbContext Local Data与AsNoTracking无跟踪查询 如何提高效率 DbContext如何关闭延迟加载 DbContext如可使用延迟加载 DbContext如何控制并发 解除属性映射到数据库中 对象失去或没有被跟踪时处理 多对多关系之扩展字段字段处理 利用模板使模型继承基类 如

Entity Framework 5.0 Code First全面学习

目录(?)[+] 不贴图片了,太累. Code First 约定 借助 CodeFirst,可通过使用 C# 或Visual Basic .NET 类来描述模型.模型的基本形状可通过约定来检测.约定是规则集,用于在使用 Code First 时基于类定义自动配置概念模型.约定是在 System.Data.Entity.ModelConfiguration.Conventions 命名空间中定义的. 可通过使用数据注释或Fluent API 进一步配置模型.优先级是通过 Fluent API 进行

也谈乐观并发控制(转)

add by zhj: 本文主要谈的是乐观并发控制,虽然乐观并发控制不太适用于并发写冲突很频繁的场景下,因为这样会导致事务回滚,需要用户重试retry, 但是如果不用乐观并发控制的话,貌似也没有其它什么好的办法了,悲观锁并不能解决更新丢失的问题,比如本文中的例子,我们也可以想想Git 遇到这种情况时是怎么处理的,其实Git也会像本文一样处理.为什么说悲观锁也不能完全解决更新丢失的问题呢?我们看下面的例子,两个用户 张三,李四,他们两人可以更新同一条数据库记录,假设记录为(sex,age) = (