当前位置: 首页 > 编程日记 > 正文

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

引言

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

目录

一、并发处理的定义

二、模型属性的并发处理选项

三、Entity Framewrok 悲观并发

四、Entity Framework 乐观并发

五、回顾 LINQ to SQL 并发处理的方式

六、结合事务处理并发冲突

一、并发处理的定义

在软件开发过程中,当多个用户同时修改一条数据记录时,系统需要预先制定对并发的处理模式。并发处理模式主要分为两种: 第一种模式称为悲观式并发,即当一个用户已经在修改某条记录时,系统将拒绝其他用户同时修改此记录。 第二种模式称为乐观式并发,即系统允许多个用户同时修改同一条记录,系统会预先定义由数据并发所引起的并发异常处理模式,去处理修改后可能发生的冲突。常用的乐观性并发处理方法有以下几种:

  1. 保留最后修改的值。
  2. 保留最初修改的值。
  3. 合并多次修改的值。

相对于LINQ TO SQL 中的并发处理方式,Entity Framework 中的并发处理方式实现了不少的简化,下面为大家一一介绍。

回到目录

二、模型属性的并发处理选项

在System.Data.Metadata.Edm 命名空间中,存在ConcurencyMode 枚举,用于指定概念模型中的属性的并发选项。 ConcurencyMode 有两个成员:

成员名称说明
        None  在写入时从不验证此属性。 这是默认的并发模式。
        Fixed在写入时始终验证此属性。

当模型属性为默认值 None 时,系统不会对此模型属性进行检测,当同一个时间对此属性进行修改时,系统会以数据合并方式处理输入的属性值。 当模型属性为Fixed 时,系统会对此模型属性进行检测,当同一个时间对属性进行修改时,系统就会激发OptimisticConcurrencyException 异常。 开发人员可以为对象的每个属性定义不同的 ConcurencyMode 选项,选项可以在*.csdl 找看到:

复制代码
 1 <Schema>
 2  ......  3  ......  4 <EntityType Name="Person">  5 <Key>  6 <PropertyRef Name="Id" />  7 </Key>  8 <Property Type="Int32" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="Identity" />  9 <Property Type="String" Name="FirstName" MaxLength="50" FixedLength="false" Unicode="true" 10 ConcurrencyMode="Fixed" /> 11 <Property Type="String" Name="SecondName" MaxLength="50" FixedLength="false" Unicode="true" /> 12 <Property Type="Int32" Name="Age" /> 13 <Property Type="String" Name="Address" MaxLength="50" FixedLength="false" Unicode="true" /> 14 <Property Type="String" Name="Telephone" MaxLength="50" FixedLength="false" Unicode="true" /> 15 <Property Type="String" Name="EMail" MaxLength="50" FixedLength="false" Unicode="true" /> 16 </EntityType> 17 </Schema>
复制代码

回到目录

 

三、Entity Framework 悲观并发

在一般的开发过程中,最常用的是悲观并发处理。.NET 提供了Lock、Monitor、Interlocked 等多个锁定数据的方式,它可以保证同一个表里的对象不会同时被多个客户进行修改,避免了系统数据出现逻辑性的错误。 由于本篇文章主要介绍并发处理方式,关于锁的介绍,请参考http://www.cnblogs.com/leslies2/archive/2012/02/08/2320914.html#t8

复制代码
 1           private static object o=new object();  2  3 public int Update(Person person)  4  {  5 int n = -1;  6 try  7  {  8 lock (o)  9  { 10 using (BusinessEntities context = new BusinessEntities()) 11  { 12 var obj = context.Person.Where(x => x.Id == person.Id).First(); 13 if (obj != null) 14 context.ApplyCurrentValues("Person", person); 15 n = context.SaveChanges(); 16  } 17  } 18  } 19 catch (Exception ex) 20  { ...... } 21 return n; 22 }
复制代码

使用悲观并发虽然能有效避免数据发生逻辑性的错误,但使用 lock 等方式锁定 Update 方法的操作,在用户同时更新同一数据表的数据,操作就会被延时或禁止。在千万级 PV 的大型网络系统当中使用悲观并发,有可能降低了系统的效率,此时可以考虑使用乐观并发处理。

 回到目录

 

四、Entity Framework 乐观并发

为了解决悲观并发所带来的问题,ADO.NET Entity Framework 提供了更为高效的乐观并发处理方式。相对于LINT to SQL , ADO.NET Entity Framework 简化了乐观并发的处理方式,它可以灵活使用合并数据、保留初次输入数据、保留最新输入数据等方式处理并发冲突。

4.1 以合并方式处理并发数据

当模型属性的 ConcurencyMode 为默认值 None ,一旦同一个对象属性同时被修改,系统将以合并数据的方式处理并发冲突,这也是 Entity Framework 处理并发冲突的默认方式。合并处理方式如下:当同一时间针对同一个对象属性作出修改,系统将保存最新输入的属性值。当同一时间对同一对象的不同属性作出修改,系统将保存已被修改的属性值。下面用两个例子作出说明:

4.1.1 同时更新数据

在系统输入下面代码,获取数据库中的Person:Id 为24,FirstName为Leslie, SecondName为Lee。然后使用异步方法分两次调用Update方法,同时更新Person对象的相关属性,第一次更新把对象的 FirstName 属性改Rose,第二次更新把对象的 SecondName改为Wang,Age改为32。在使用SaveChanges保存更新时,第一个方法已经把ObjectContext1中的FirstName修改为Rose,但在第二个方法中的ObjectContext2中的FirstName依然是Leslie,此时由于三个属性的ConcurencyMode都为默认值None,系统会忽略当中的冲突,而接受所有的更新修改。

复制代码
 1      public class PersonDAL
 2  {  3 public Person GetPerson(int id)  4  {  5 using (BusinessEntities context = new BusinessEntities())  6  {  7 IQueryable<Person> list=context.Person.Where(x => x.Id == id);  8 return list.First();  9  } 10  } 11 12 public void Update(Person person) 13  { 14 using (BusinessEntities context = new BusinessEntities()) 15  { 16 //显示输入新数据的信息 17 Display("Current", person); 18 var obj = context.Person.Where(x => x.Id == person.Id).First(); 19 if (obj != null) 20 context.ApplyCurrentValues("Person", person); 21 22 //虚拟操作,保证数据能同时加入到上下文当中 23 Thread.Sleep(100); 24  context.SaveChanges(); 25  } 26  } 27 28 delegate void MyDelegate(Person person); 29 30 public static void Main(string[] args) 31  { 32 //在更新数据前显示对象信息 33 PersonDAL personDAL = new PersonDAL(); 34 var beforeObj = personDAL.GetPerson(24); 35 personDAL.Display("Before", beforeObj); 36 37 //更新Person的SecondName,Age两个属性 38 Person person1 = new Person(); 39 person1.Id = 24; 40 person1.FirstName = "Leslie"; 41 person1.SecondName = "Wang"; 42 person1.Age = 32; 43 person1.Address = "Tianhe"; 44 person1.Telephone = "13660123456"; 45 person1.EMail = "Leslie@163.com"; 46 47 //更新Person的FirstName属性 48 Person person2 = new Person(); 49 person2.Id = 24; 50 person2.FirstName = "Rose"; 51 person2.SecondName = "Lee"; 52 person2.Age = 34; 53 person2.Address = "Tianhe"; 54 person2.Telephone = "13660123456"; 55 person2.EMail = "Leslie@163.com"; 56 57 //使用异步方式同时更新数据 58 MyDelegate myDelegate = new MyDelegate(personDAL.Update); 59 myDelegate.BeginInvoke(person1, null, null); 60 myDelegate.BeginInvoke(person2, null, null); 61 62 Thread.Sleep(300); 63 //在更新数据后显示对象信息 64 var afterObj = personDAL.GetPerson(24); 65 personDAL.Display("After", afterObj); 66  Console.ReadKey(); 67  } 68 69 public void Display(string message,Person person) 70  { 71 String data = string.Format("{0}\n Person Message:\n Id:{1} FirstName:{2} "+ 72 "SecondName:{3} Age:{4}\n Address:{5} Telephone:{6} EMail:{7}\n", 73  message, person.Id, person.FirstName, person.SecondName, person.Age, 74  person.Address, person.Telephone, person.EMail); 75  Console.WriteLine(data); 76  } 77 }
复制代码

根据操作结果可以看到,在Entity Framework的默认环境情况下,系统会使用合并方式处理并发,把输入数据的所有修改值都保存到当前上下文当中,并同时修改数据库当中的值。

4.1.2 删除与更新操作同时运行

Entity Framework 能以完善的机制灵活处理同时更新同一对象的操作,但一旦删除操作与更新操作同时运行时,就可能存在逻辑性的异常。例如:两个客户端同时加载了同一个对象,第一个客户端更新了数据后,把数据再次提交。但在提交前,第二个客户端已经把数据库中的已有数据删除。此时,上下文中的对象处于不同的状态底下,将会引发 OptimisticConcurrencyException 异常。 遇到此异常时,可以用 try(OptimisticConcurrencyException){...} catch {...} 方式捕获异常,然后使用 ObjectStateManager.ChangeObjectState 方法更改对象的 EntityState 属性。把EntityState 更改为 Added ,被删除的数据便会被再次加载。若把 EntityState 更改为 Detached 时,数据便会被顺利删除。下面把对象的 EntityState 属性更改为 Added 作为例子。

复制代码
  1     public class PersonDAL
  2  {  3 delegate int MyDelegate(Person person);  4  5 public static void Main(string[] args)  6  {  7 //在更新数据前显示对象信息  8 PersonDAL personDAL = new PersonDAL();  9 var beforeObj = personDAL.GetPerson(51);  10 personDAL.DisplayProperty("Begin", beforeObj);  11  12 //更新Person的属性  13 Person person1 = new Person();  14 person1.Id = 51;  15 person1.FirstName = "Leslie";  16 person1.SecondName = "Wang";  17 person1.Age = 32;  18 person1.Address = "Tianhe";  19 person1.Telephone = "13660123456";  20 person1.EMail = "Leslie@163.com";  21  22 //使用异步方式更新数据  23 MyDelegate myDelegate = new MyDelegate(personDAL.Update);  24 IAsyncResult reslut=myDelegate.BeginInvoke(person1, null, null);  25  26 //同步删除原有数据  27 personDAL.Delete(51);  28 //显示删除后重新被加载的数据  29 var afterObj = personDAL.GetPerson(myDelegate.EndInvoke(reslut));  30 personDAL.DisplayProperty("End", afterObj);  31  }  32  33 public Person GetPerson(int id)  34  {  35 using (BusinessEntities context = new BusinessEntities())  36  {  37 IQueryable<Person> list=context.Person.Where(x => x.Id == id);  38 return list.First();  39  }  40  }  41  42 //更新对象  43 public int Update(Person person)  44  {  45 int returnValue=-1;  46 using (BusinessEntities context = new BusinessEntities())  47  {  48 var obj = context.Person.Where(x => x.Id == person.Id).First();  49 //显示对象所处状态  50 DisplayState("Before Update", obj);  51 try  52  {  53 if (obj != null)  54 context.ApplyCurrentValues("Person", person);  55 //虚拟操作,保证数据已经在数据库中被异步删除  56 Thread.Sleep(100);  57  context.SaveChanges();  58 returnValue = obj.Id;  59  }  60 catch (System.Data.OptimisticConcurrencyException ex)  61  {  62 //把对象的状态更改为 Added  63  context.ObjectStateManager.ChangeObjectState(obj, System.Data.EntityState.Added);  64  context.SaveChanges();  65 returnValue=obj.Id;  66  }  67  }  68 return returnValue;  69  }  70  71 //删除对象  72 public void Delete(int id)  73  {  74 using (BusinessEntities context = new BusinessEntities())  75  {  76 var person1 = context.Person.Where(x => x.Id == id).First();  77 if (person1 != null)  78  context.Person.DeleteObject(person1);  79  context.SaveChanges();  80 //显示对象现在所处的状态  81 DisplayState("After Delete:", person1);  82  }  83  } 

转载于:https://www.cnblogs.com/MarchThree/p/3840559.html

相关文章:

@2021高考生,用 Python 分析专业“钱景”

来源 | 关于数据分析于可视化头图 | 下载于ICphoto2021年的高考在昨日拉开帷幕&#xff0c;十年的寒窗苦读&#xff0c;终于到了最后见分晓的时候了。在这么一场关键的考试当中&#xff0c;除了考试前努力奋斗&#xff0c;考场上认真答题&#xff0c;考后的志愿填报也是极其的重…

Linux下C语言的fgets与fputs

使用的是 CentOS gcc编译下面程序 显示warning: the gets function is dangerous and should not be used.问题出在程序中使用了 gets Linux 下gcc编译器不支持这个函数&#xff0c;解决办法是使用 fgets fgets()函数的基本用法为&#xff1a; fgets(char * s,int size,FILE * …

linux发行版的用户交互

1 cli&#xff0c;即command line interface 纯命令行的交互方式&#xff0c;该命令行界面是由shell提供的。 linux内核本身也自带了一个console&#xff0c;即linux console&#xff0c;它是基于frame buffer的。 cli的界面都是基于ncurses库开发的。 2 GUI&#xff0c;graphi…

C#中在应用程序和DLL使用消息

在C#中采用的是事件驱动方式&#xff0c;但在我们使用的过程中&#xff0c;有时候通过调用系统原有的消息&#xff0c;处理起来会比较简单一些&#xff0c;特别是在处理与DLL文件的交互时&#xff0c;的确是非常的方便。 在C#中使用自定义消息 在C#中使用自定义消息非常简单&…

倪光南院士:openEuler与全球开发者共同推动计算产业发展

今日&#xff0c;以“创造最好的OS&#xff0c;成就更好的我们”为主题的 openEuler Developer Day 2021在北京成功举行。本次大会由openEuler社区发起&#xff0c;产业组织专家、学者、企业领袖和开发者们围绕多样性计算、云原生全栈、全场景协同等技术方向共同探讨和创新。大…

jhello框架-ajax

2019独角兽企业重金招聘Python工程师标准>>> 现在的web开发没有ajax都没法活&#xff0c;所以在jhello中实现了一个简单的ajax交互&#xff0c;使用json作为载体。 在上篇《交互》中讲到一种交互的方式是把数据放入Model中&#xff0c;通过ModelAndView类返回到视图…

发微信模版消息换行用\n

发微信模版消息换行用\n发微信模版消息换行用\n发微信模版消息换行用\n发微信模版消息换行用\n发微信模版消息换行用\n转载于:https://blog.51cto.com/xuqin/1974131

二次元会让人脸识别失效吗?

来源 | PyTorch 开发者社区责编 | 寇雪芹头图 | 下载于ICphoto人脸识别也遇到坑了&#xff0c;识别得了三次元&#xff0c;却对二次元无效。迪士尼的技术团队&#xff0c;正在开发这一算法&#xff0c;以帮助动画制作者进行后期搜索。团队利用 PyTorch&#xff0c;效率得到很大…

基于第四层交换技术的负载均衡

摘 要 本文介绍了第四层交换技术的概念&#xff0c;技术原理以及如何使用第四层交换技术实现远程教育系统中的应用服务器负载均衡。 作者&#xff1a; 凌仲权&#xff0c;现就读于西安电子科技大学&#xff0c;2001级硕士研究生。主要的科研方向为计算机网络技术研究以及远程教…

開始Unity3D的学习之旅

前言&#xff1a;这个系列的文章纯属对自己学习的整理&#xff0c;非高手之作。但确实的记载了我作为一个没接触过3D游戏编程的大学生的心路历程。争取每周整理一次吧。之所以会開始学Unity3D&#xff0c;最基本的原因是由于在快放暑假的时候&#xff0c;我找了一家做iPhone游戏…

linux 定时任务crond

1.定时任务crond 1.1 crond是什么 crond是linux系统中用来定期执行命令或指定程序任务的一种服务或软件。特殊需求&#xff1a;&#xff08;秒级别&#xff09;crond服务就无法搞定了&#xff0c;一般工作中写脚本守护进程执行。 为什么要使用crond定时任务&#xff1a; linux系…

用C#去除代码的SourceSafe管理

经常看一些的程序&#xff0c;有些一个解决方案带有多个项目&#xff0c;由于代码比较多&#xff0c;多人开发&#xff0c;所以好多vs.net下的工程是用source safe进行版本控制的。而用source safe进行版本控制需要局域网路径共享&#xff0c;因此好多项目换一台机器打开会出现…

用jarsigner对android apk进行签名

以前对apk重新打包签名都是用的apktool里面的Auto-sign 工具&#xff0c;后来发现有时候利用该工具对一些apk签名会失败&#xff0c;所以后来就使用 jarsigner这个工具&#xff0c;现记录一下这个工具的使用方法1 首先要生成一个自己的keyD:\>keytool -genkey -alias myKey…

利用java反射机制 读取配置文件 实现动态类载入以及动态类型转换

作者&#xff1a;54dabang 在spring的学习过程之中&#xff0c;我们能够看出通过配置文件来动态管理bean对象的优点&#xff08;松耦合 能够让零散部分组成一个总体&#xff0c;而这些总体并不在意之间彼此的细节&#xff0c;从而达到了真正的物理上的疏散耦合&#xff0c;而非…

C#操作消息队列

public class QueueManage { /// /// 发送对象到队列中 /// /// 队列名称&#xff0c;因为队列名称在一个应用中应该不改变的&#xff0c;所以大家最好写在配置文件中 /// 要发出去的对象 public static void SendQueue(string QueuePath,MyBase.SmsQueue sq) { Syste…

2021《程序员》数字科技企业研发实力榜TOP50

互联网的盛行带来了众多数字科技企业的崛起&#xff0c;但如何客观地衡量每家企业的技术实力&#xff1f;研发投入、研发人才的数量和人才密度是关键指标。2021年5月,《程序员》根据相关企业披露的财报数据及市场调研数据&#xff0c;整理发布“2021数字科技企业研发实力榜TOP5…

eclipse中java项目转换为web项目

2019独角兽企业重金招聘Python工程师标准>>> 经常在eclipse中导入web项目时&#xff0c;出现转不了项目类型的问题&#xff0c;导入后就是一个java项目&#xff0c;有过很多次经历&#xff0c;今天也有同事遇到类似问题&#xff0c;就把这个解决方法记下来吧&#x…

MySQL · 引擎介绍 · Sphinx源码剖析(三)

在本节中我会介绍Sphinx在构建索引之前做的一些事情&#xff0c;主要是从mysql拉取数据保存&#xff0c;然后分词排序保存到内存等等一系列的操作。下面是几个相关指令 sql_query \SELECT id, group_id, UNIX_TIMESTAMP(date_added) AS date_added, \title, content \FROM doc…

C#给图片添加版权信息

现在越来越多的网站都喜欢将用户上传的图片加上网站的版权信息&#xff0c;不要以为那是用photoshop之类的图片处理软件加上去的&#xff0c;其实我们只要写一小段代码&#xff0c;就可以实现这个功能。 添加版权信息的原理其实挺简单&#xff1a;通过图片获取Graphics类的对象…

Python + 爬虫:可视化大屏帮你选粽子

来源 | 数据分析与统计学之美头图 | 下载于ICphoto端午节快要到了&#xff0c;旅游&#xff1f;回家&#xff1f;拜访亲友&#xff1f;少不了要带上粽子。那么&#xff0c;选择什么牌子的粽子呢&#xff1f;选择什么口味的粽子呢&#xff1f;选择什么价格区间呢&#xff1f;今年…

adviser vs mentor

研究生或博士生提到自己导师的时候是说adviser呢&#xff1f;还是mentor呢&#xff1f; 至少我认识一个Berkeley的博士是说adviser的。 另外&#xff0c;我的导师也是说adviser。 那还是说adviser吧……

T extends Serializable这是什么意思呢?看明白这个,你的问题就自然而然的明白了!...

1.转自&#xff1a;https://blog.csdn.net/liwenqiang758/article/details/8131185 自己动手丰衣足食!!! 泛型是Java SE 1.5的新特性&#xff0c;泛型的本质是参数化类型&#xff0c;也就是说所操作的数据类型被指定为一个参数。 这种参数类型可以用在类、接口和方法的创建中&a…

C#中switch语句注意

大家肯定对switch语句并不陌生&#xff0c;它能够让程序根据控制表达式的值&#xff0c;从多个动作中作出选择(从逻辑过程看&#xff0c;和多分支语句if&#xff0d;else有些相似)。在C和java中也有这一语句&#xff0c;不过在C#中&#xff0c;这一语句则有了些变化。先看下边一…

对Cost (%CPU) 粗略的理解

今天研究执行计划&#xff0c;看到执行计划里面有Cost (%CPU)&#xff0c;我这边研究了一把&#xff0c;不知道对与否&#xff0c;拿出来晒晒在Oracle 10g中&#xff0c;Oracle 把CPU的cost也统计在执行计划中去了&#xff0c; 这和以前的8i,9i(9i其实已经开始了)有很大的不同。…

后疫情时代,RTC期待新的场景大爆发

整理 | 寇雪芹头图 | 下载于ICphoto出品 | AI 科技大本营&#xff08;ID:rgznai100&#xff09; 过去的一年中&#xff0c;新冠疫情悄然改变了社会发展和人们的生活&#xff0c;也助推了实时音视频&#xff08;Real-Time Communication&#xff09;技术的落地应用&#xff0c;数…

JS-DOM-元素节点

查看元素节点: 1、getElementById():通过 id 取到唯一节点;如果 id 重名,只能取到第一个 getElementByName(): 通过name属性 getElementByTagName(): 通过标签名 getElementByClassName(): 通过class名 获取元素节点时,一定要注意:获取节点的语句,必须在 DOM 渲染完成之后执行。…

现代软件工程 第十章 【典型用户和场景】 练习与讨论

1. 讨论&#xff1a;下面的老板犯了什么错误? 只看用户的表面语言或行动还是不够的。我们还要找到用户语言行动背后的动机! (图像来源: http://www.weibo.com/funnyshoelace) 2. 是否要文档 有人说&#xff0c;我们敏捷的团队&#xff0c;就喜欢直接的面对面的交流&#xff0…

赠书 | 读懂生成对抗神经网络 GAN,看这文就够了

生成对抗神经网络&#xff08;Generative Adversarial Nets&#xff0c;GAN&#xff09;是一种深度学习的框架&#xff0c;它是通过一个相互对抗的过程来完成模型训练的。典型的GAN包含两个部分&#xff0c;一个是生成模型&#xff08;Generative Model&#xff0c;简称G&#…

把Doc文档转换成rtf格式

先在项目引用里添加上对Microsoft Word 9.0 object library的引用。 using System; namespace DocConvert { class DoctoRtf { static void Main() { //创建一个word的实例 Word.Application newApp new Word.Application(); // 指定源文件和目标文件 object Source&quo…