转载自:http://www.cnblogs.com/TianFang/p/4439215.html

什么是并发?

并发分悲观并发和乐观并发。

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

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

EF中如何控制并发?

Entity Framework不支持悲观并发,只支持乐观并发。

如果要对某一个表做并发处理,就在该表中加一条Timestamp类型的字段。注意,一张表中只能有一个Timestamp的字段。

Data Annotations中用Timestamp来标识设置并发控制字段,标识为Timestamp的字段必需为byte[]类型。

  1. public class Person
  2. {
  3. public int PersonId { get; set; }
  4. public int SocialSecurityNumber { get; set; }
  5. public string FirstName { get; set; }
  6. public string LastName { get; set; }
  7. [Timestamp]
  8. public byte[] RowVersion { get; set; }
  9. }

Fluent API用IsRowVersion方法

  1. modelBuilder.Entity<Person>().Property(p => p.RowVersion).IsRowVersion();

我们看到生成的数据库中,RowVersion是timestamp类型。

下面我们写一段代码来测试一下:

  1. static void Main(string[] args)
  2. {
  3. var person = new Person
  4. {
  5. FirstName = "Rowan",
  6. LastName = "Miller",
  7. SocialSecurityNumber = 12345678
  8. };
  9. //新增一条记录,保存到数据库中
  10. using (var con = new BreakAwayContext())
  11. {
  12. con.People.Add(person);
  13. con.SaveChanges();
  14. }
  15.  
  16. var firContext = new BreakAwayContext();
  17. //取第一条记录,并修改一个字段:这里是修改了FirstName
  18. //先不保存
  19. var p1 = firContext.People.FirstOrDefault();
  20. p1.FirstName = "Steven";
  21.  
  22. //再创建一个Context,同样取第一条记录,修改LastName字段并保存
  23. using (var secContext = new BreakAwayContext())
  24. {
  25. var p2 = secContext.People.FirstOrDefault();
  26. p2.LastName = "Francis";
  27. secContext.SaveChanges();
  28. }
  29. try
  30. {
  31. firContext.SaveChanges();
  32. Console.WriteLine(" 保存成功");
  33. }
  34. catch (DbUpdateConcurrencyException ex)
  35. {
  36. Console.WriteLine(ex.Entries.First().Entity.GetType().Name + " 保存失败");
  37. }
  38. Console.Read();
  39. }

上面我们实例化了三个DbContext,第一个增加一条记录到数据库中,第二个修改刚增加的记录但不保存,然后第三个Context也取刚新增的记录并保存,最后再保存第二个Context,结果保存失败。

可以看到我们的并发控制取到了作用。

分析EF生成的SQL语句:

  1. exec sp_executesql N'update [dbo].[People]
  2. set [LastName] = @0
  3. where (([PersonId] = @1) and ([RowVersion] = @2))
  4. select [RowVersion]
  5. from [dbo].[People]
  6. where @@ROWCOUNT > 0 and [PersonId] = @1',N'@0 nvarchar(max) ,@1 int,@2 binary(8)',@0=N'Francis',@1=1,@2=0x00000000000007D1

可以看到,它在取对应记录的时候把RowVersion也作为筛选条件。上面例子中的secContext保存的时候,数据库中的RowVersion字段的值就变了,所以firContext保存的时候用原来的RowVersion取值,自然就取不到相应的记录而报错。

如果我们只是要对某个字段作并发控制呢?别着急,EF也有办法。

Data Annotations中用ConcurrencyCheck来标识

  1. public class Person
  2. {
  3. public int PersonId { get; set; }
  4. [ConcurrencyCheck]
  5. public int SocialSecurityNumber { get; set; }
  6. public string FirstName { get; set; }
  7. public string LastName { get; set; }
  8. public byte[] RowVersion { get; set; }
  9. }

Fluent API用IsConcurrencyToken方法

  1. modelBuilder.Entity<Person>().Property(p => p.SocialSecurityNumber).IsConcurrencyToken();

上面的实体中,我们将SocialSecurityNumber(社会保险号)标识为开放式并发,也写一个类似的代码测试一下:

  1. static void Main(string[] args)
  2. {
  3. var person = new Person
  4. {
  5. FirstName = "Rowan",
  6. LastName = "Miller",
  7. SocialSecurityNumber = 12345678
  8. };
  9. //新增一条记录,保存到数据库中
  10. using (var con = new BreakAwayContext())
  11. {
  12. con.People.Add(person);
  13. con.SaveChanges();
  14. }
  15.  
  16. var firContext = new BreakAwayContext();
  17. //取第一条记录,并修改SocialSecurityNumber字段
  18. //先不保存
  19. var p1 = firContext.People.FirstOrDefault();
  20. p1.SocialSecurityNumber = 123;
  21.  
  22. //再创建一个Context,同样取第一条记录,
  23. //修改SocialSecurityNumber字段并保存
  24. using (var secContext = new BreakAwayContext())
  25. {
  26. var p2 = secContext.People.FirstOrDefault();
  27. p2.SocialSecurityNumber = 456;
  28. secContext.SaveChanges();
  29. }
  30. try
  31. {
  32. firContext.SaveChanges();
  33. Console.WriteLine(" 保存成功");
  34. }
  35. catch (DbUpdateConcurrencyException ex)
  36. {
  37. Console.WriteLine(ex.Entries.First().Entity.GetType().Name + " 保存失败");
  38. }
  39. Console.Read();
  40. }

运行结果同样是保存失败,说明我们的并发控制起作用了。

分析一下EF执行的SQL:

  1. exec sp_executesql N'update [dbo].[People]
  2. set [SocialSecurityNumber] = @0
  3. where (([PersonId] = @1) and ([SocialSecurityNumber] = @2))
  4. ',N'@0 int,@1 int,@2 int',@0=123,@1=1,@2=12345678

可以看到,EF将我们要并发控制的列SocialSecurityNumber也作为一个筛选条件,这样firContext保存的时候也会因为的数据库中SocialSecurityNumber值变了,取不到对应的记录而更新失败。

补充一下:如果是EDMX如何将字段设置为Concurrency。很简单,在对应的字段上右键-属性。在打开的属性窗口中有一个并发模式,你将它选择为Fixed即可。

EntityFramework 并发处理的更多相关文章

  1. EF CodeFirst(三) 并发处理

    并发分为两种,一种叫做悲观并发,一种叫乐观并发. 名字挺文艺 悲观并发 悲观并发是什么呢? 就拿我们常用的代码版本控制来说. 有一个文档,A和B都要 获取这个文档并进行修改, 如果当A在读取这个文档数 ...

  2. EntityFramework Core Raw SQL

    前言 本节我们来讲讲EF Core中的原始查询,目前在项目中对于简单的查询直接通过EF就可以解决,但是涉及到多表查询时为了一步到位就采用了原始查询的方式进行.下面我们一起来看看. EntityFram ...

  3. 恋爱虽易,相处不易:当EntityFramework爱上AutoMapper

    剧情开始 为何相爱? 相处的问题? 女人的伟大? 剧情收尾? 有时候相识即是一种缘分,相爱也不需要太多的理由,一个眼神足矣,当EntityFramework遇上AutoMapper,就是如此,恋爱虽易 ...

  4. 关于这段时间学习 EntityFramework的 一点感悟

    Ado.Net,用了N多年,Entity Framework也关注了很多年. 每当项目转型的时候,就花费大巴的时间,学习一番,潮流的东西. 这个Orm很多,这个EF很火,这么多年了,我还是不敢用,虽然 ...

  5. 采用EntityFramework.Extended 对EF进行扩展(Entity Framework 延伸系列2)

    前言 Entity Framework 延伸系列目录 今天我们来讲讲EntityFramework.Extended 首先科普一下这个EntityFramework.Extended是什么,如下: 这 ...

  6. 一次修改闭源 Entity Provider 程序集以兼容新 EntityFramework 的过程

    读完本文你会知道,如何在没有源码的情况下,直接修改一个 DLL 以去除 DLL 上的强命名限制,并在该程序集上直接添加你的“友元程序集(一种特殊的 Attribute,将它应用在程序集上,使得程序集内 ...

  7. ABP文档 - EntityFramework 集成

    文档目录 本节内容: Nuget 包 DbContext 仓储 默认仓储 自定义仓储 特定的仓储基类 自定义仓储示例 仓储最佳实践 ABP可使用任何ORM框架,它已经内置了EntityFrame(以下 ...

  8. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  9. 神马玩意,EntityFramework Core 1.1又更新了?走,赶紧去围观

    前言 哦,不搞SQL了么,当然会继续,周末会继续更新,估计写完还得几十篇,但是我会坚持把SQL更新完毕,绝不会烂尾,后续很长一段时间没更新的话,不要想我,那说明我是学习新的技能去了,那就是学习英语,本 ...

随机推荐

  1. OVS的初始配置

    1.去掉bridge模块,为下面用OVS的模块奠定基础 rmmod bridge .insmod datapath/linux/openvswitch_mod.ko .insmod datapath/ ...

  2. Unity3D面试题整合

    第一部分 1. 请简述值类型与引用类型的区别答:区别:1.值类型存储在内存栈中,引用类型数据存储在内存堆中,而内存单元中存放的是堆中存放的地址.2.值类型存取快,引用类型存取慢.3.值类型表示实际数据 ...

  3. Alpha冲刺(二)

    Information: 队名:彳艮彳亍团队 组长博客:戳我进入 作业博客:班级博客本次作业的链接 Details: 组员1(组长)柯奇豪 过去两天完成了哪些任务 学习并配置了ssm框架(用于前后端交 ...

  4. Spring MVC:Model、View、ModelAndView

    个人理解:View为服务器上的某个文件容器,可以为JSP,FTL等动态页面文件,甚至是媒体文件等等,单单是一个文件.Model的作用是存储动态页面属性,动态页面文件即View可以在Model中获取动态 ...

  5. C# 属性与字段

    属性和字段的区别: 属性是逻辑字段,是字段的扩展,并不占用实际的内存:而字段占用内存空间. 属性可以被其他类访问:而非public的字段不能被直接访问. 属性可以对接受的数据在范围上做限定:而字段不能 ...

  6. Launch VINS-Mono with Realsense D435i in RTAB-Map

    Preparation: Remap topic from D435i to rtabmap Feed the odometry to rtabmap In the rqt_graph of vins ...

  7. spring boot日志及Log4j日志配置

    1.默认实现的日志配置 Spring boot默认已经集成了logging,同时也是默认开启的,如果想根据自己的需求对日志进行配置,方法很简单--只需要在配置文件中进行相应设置,这里提供我自己的配置如 ...

  8. ASP Session 对象

    http://www.w3school.com.cn/asp/asp_sessions.asp

  9. SHELL编程规范与变量

    shell相比标准开发语言比如JAVA,C,C++来说还是比较简单的,其实就是一堆命令的集合而已,初学者从简单的shell脚本学起,至于shell编程还是要有编程思想在里面,因此,循序渐进很重要,先研 ...

  10. 【算法】关于图论中的最小生成树(Minimum Spanning Tree)详解

    本节纲要 什么是图(network) 什么是最小生成树 (minimum spanning tree) 最小生成树的算法 什么是图(network)? 这里的图当然不是我们日常说的图片或者地图.通常情 ...