学习:C#综合揭秘——Entity Framework 并发处理详解 帖子笔记 ,该帖子使用的是objectContext ,

一、并发相关概念

并发的类型:

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

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

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

如下图模型设计器中TimeStamp字段为启用并发

<EntityType Name="UserAccout"><Key><PropertyRef Name="Id" /></Key><Property Name="Id" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" /><Property Name="FirstName" Type="String" Nullable="false" /><Property Name="LastName" Type="String" Nullable="false" /><Property Name="AuditFileds" Type="OrderDB.AuditFields" Nullable="false" /><Property Name="Timestamp" Type="DateTime" Nullable="false" ConcurrencyMode="Fixed" annotation:StoreGeneratedPattern="Computed" /></EntityType>

并发模式:ConcurencyMode 有两个成员:

None : 在写入时从不验证此属性。 这是默认的并发模式。

Fixed: 在写入时始终验证此属性。

当模型属性为默认值 None 时,系统不会对此模型属性进行检测,当同一个时间对此属性进行修改时,系统会以数据合并方式处理输入的属性值。

当模型属性为Fixed 时,系统会对此模型属性进行检测,当同一个时间对属性进行修改时,系统就会激发OptimisticConcurrencyException 异常。

三、悲观并发

四、乐观并发

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

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

总结:当模型属性的 ConcurencyMode 为默认值 None ,一旦同一个对象属性同时被修改,系统将以合并数据的方式处理并发冲突,这也是 Entity Framework 处理并发冲突的默认方式。

合并处理方式如下:

(1)当同一时间针对同一个对象属性作出修改,系统将保存最新输入的属性值。

(2)当同一时间对同一对象的不同属性作出修改,系统将保存已被修改的属性值。下面用两个例子作出说明:

运行结果:

#region (4.1)测试不设置任何并发测试时,当产生并发EF的处理方法delegate void MyDelegate(Address addressValue);public  StringBuilder sb = new StringBuilder();public Address GetAddress(int id){using (OrderDBContainer context = new OrderDBContainer()){IQueryable<Address> list = context.AddressSet.Where(x => x.Id == id);return list.First();}}/// <summary>/// 修改方法/// </summary>/// <param name="addressValue"></param>public void UpdateAddress(Address addressValue){using (OrderDBContainer context = new OrderDBContainer()){//显示输入新数据的信息Display("Current", addressValue);var obj = context.AddressSet.Where(x => x.Id == addressValue.Id).First();if (obj != null)context.Entry(obj).CurrentValues.SetValues(addressValue);//虚拟操作,保证数据能同时加入到上下文当中Thread.Sleep(100);context.SaveChanges();}}        /// <summary>/// 显示实体当前属性/// </summary>/// <param name="message"></param>/// <param name="addressValue"></param>public void Display(string message, Address addressValue){String data = string.Format("{0}\n  Address Message:\n    Id:{1}  Address1:{2}  " +"address2:{3} \r\n ",message, addressValue.Id, addressValue.Address1, addressValue.Address2 );sb.AppendLine(data);}     /// <summary>/// (1)测试使用EF默认的机制,当配置并发控制时,系统是使用的合并的方式/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button3_Click(object sender, EventArgs e){//在更新数据前显示对象信息var beforeObj = GetAddress(1);Display("Before", beforeObj);//更新Person的SecondName,Age两个属性Address _address1 = new Address();_address1.Id = 1;_address1.Address1 = "古溪";_address1.Address2 = beforeObj.Address2;_address1.AuditFields.InsertDate = beforeObj.AuditFields.InsertDate;_address1.AuditFields.UpdateDate = beforeObj.AuditFields.UpdateDate;_address1.City = beforeObj.City;_address1.Zip = beforeObj.Zip;_address1.State = beforeObj.State;//更新Person的FirstName属性Address _address2 = new Address();_address2.Id = 1;_address2.Address1 = beforeObj.Address1;_address2.Address2 = "江苏";_address2.AuditFields.InsertDate = beforeObj.AuditFields.InsertDate;_address2.AuditFields.UpdateDate = beforeObj.AuditFields.UpdateDate;_address2.City = beforeObj.City;_address2.Zip = beforeObj.Zip;_address2.State = beforeObj.State;//使用异步方式同时更新数据MyDelegate myDelegate = new MyDelegate(UpdateAddress);myDelegate.BeginInvoke(_address1, null, null);myDelegate.BeginInvoke(_address2, null, null);Thread.Sleep(1000);//在更新数据后显示对象信息var afterObj = GetAddress(1);Display("After", afterObj);this.textBox1.Text = sb.ToString();}/// <summary>/// 先插入几条数据等着测试/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void BtnSaveAddress_Click(object sender, EventArgs e){using (OrderDBContainer db = new OrderDBContainer()){Address address = new Address();address.Address1 = "古溪镇";address.Address2 = "安镇";address.State = "2";address.City = "无锡";address.AuditFields.InsertDate = DateTime.Now;address.AuditFields.UpdateDate = DateTime.Now;address.Zip = "21415";db.AddressSet.Add(address);db.SaveChanges();}}/// <summary>/// 还原成初始值,准备再次测试/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button5_Click(object sender, EventArgs e){using (OrderDBContainer db = new OrderDBContainer()){Address _address = db.AddressSet.Where(x => x.Id == 1).First();_address.Address1 = "aaa";_address.Address2 = "bbb";db.SaveChanges();}}#endregion

备注:实践过程中遇到的问题

在多线程中EF修改事件的解决方案,使用attach不可以:

使用Entry也报错

最终参考如下帖子

/// <summary>/// 修改方法/// </summary>/// <param name="addressValue"></param>public void UpdateAddress(Address addressValue){using (OrderDBContainer context = new OrderDBContainer()){//显示输入新数据的信息Display("Current", addressValue);var obj = context.AddressSet.Where(x => x.Id == addressValue.Id).First();if (obj != null)context.Entry(obj).CurrentValues.SetValues(addressValue);//虚拟操作,保证数据能同时加入到上下文当中Thread.Sleep(100);context.SaveChanges();}}

引用:“以合并数据的方式处理并发冲突固然方便快节,但在业务逻辑较为复杂的系统下并不适合使用此处理方式。比如在常见的Order、OrderItem的表格中,OrderItem 的单价,数量会直接影响Order的总体价格,这样使用合并数据的方式处理并发,有可能引起逻辑性的错误。此时,应该考虑以其他方式处理并发冲突。”。

其他什么方式呢?【待补充】

4.1 删除与更新操作同时运行(非框架自动处理能力,开发自行修改状态手动增加的)

Entity Framework 能以完善的机制灵活处理同时更新同一对象的操作,但一旦删除操作与更新操作同时运行时,就可能存在逻辑性的异常。

例如:两个客户端同时加载了同一个对象,第一个客户端更新了数据后,把数据再次提交。但在提交前,第二个客户端已经把数据库中的已有数据删除。

此时,上下文中的对象处于不同的状态下,将会引发 OptimisticConcurrencyException 异常(ObjectContext 与DBContext两种方式下,异常不一样,具体要根据测试结果自己判断)。
遇到此异常时,可以用 try(OptimisticConcurrencyException){...} catch {...} 方式捕获异常,然后更改对象的State 属性。把EntityState 更改为 Added ,被删除的数据便会被再次加载。若把 EntityState 更改为 Detached 时,数据便会被顺利删除。下面把对象的 EntityState 属性更改为 Added 作为例子。

代码如下:处理结果前后ID变化了(或许这就是有些架构师使用手动创建的GUID的方式,而不使用自增的原因之一吧,因为数据删除后再创建就回不到之前的ID了,不是太灵活,使用GUID再结合数据版本(dataVison)字段,timeStamp基本上控制数据的并发已经足够啊。

//更新对象public int UpdateWithConcurrent(int num, Address addressValue){int returnValue = -1;using (OrderDBContainer context = new OrderDBContainer()){var obj = context.AddressSet.Where(x => x.Id == addressValue.Id).First();//显示对象所处状态DisplayState("Before Update", obj);try{if (obj != null)context.Entry(obj).CurrentValues.SetValues(addressValue);//虚拟操作,保证数据已经在数据库中被异步删除Thread.Sleep(300);context.SaveChanges();returnValue = obj.Id;}catch (Exception){//针对异常要做相应的判断,因为我只测试了删除的情况,就写死直接修改成Added 了//正确的是要区分到底是修改还是删除  OptimisticConcurrencyException ex//把对象的状态更改为 Addedcontext.Entry(obj).State = System.Data.Entity.EntityState.Added;context.SaveChanges();returnValue = obj.Id;}}return returnValue;}

并发时的异常类型:

ID发生了变化

4.3 当发生数据并发时,保留最终(最新:最后一次)输入的数据

要验证输入对象的属性,必须先把该属性的 ConcurencyMode 设置为 Fixed,这样系统就会实时检测对象属性的输入值 。
当该属性被同时更新,系统便会激发 OptimisticConcurrencyException 异常。捕获该异常后,可以使用 ObjectContext.Refresh (RefreshMode,object) 刷新上下文中该对象的状态,当 RefreshMode 为 ClientWins 时,系统将会保持上下文中的现在有数据,即保留最新输入的对象值。此时再使用ObjectContext.SaveChanges, 系统就会把最新输入的对象值加入数据库当中。

在下面的例子当,系统启动前先把 Person 的 FirstName、SecondName 两个属性的 ConcurencyMode 属性设置为Fixed,使系统能监视这两个属性的更改。所输入的数据只在FirstName、SecondName 两个值中作出修改。在数据提交前先以 DisplayProperty 方法显示数据库最初的数据属性,在数据初次更新后再次调用 DisplayProperty 显示更新后的数据属性。在第二次更新数据时,由调用ObjectContext.SaveChanges时,数据库中的数据已经被修改,与当前上下文ObjectContext 的数据存在冲突,系统将激发OptimisticConcurrencyException 异常,此时把引发异常的对象属性再次显示出来。对异常进行处理后,显示数据库中最终的对象值。

观察测试结果,可见当RefreshMode状态为ClientWins时,系统将会保存上下文当中的对象属性,使用此方法可以在发生并发异常时保持最新输入的对象属性。

4.4 当发生数据并发时,保留最早(最初:最早一次)输入的数据

把对象属性的 ConcurencyMode 设置为 Fixed 后,同时更新该属性,将会激发 OptimisticConcurrencyException 异常。此时使用 ObjectContext.Refresh (RefreshMode,object) 刷新上下文中该对象的状态,当 RefreshMode 为 StoreWins 时,系统就会把数据源中的数据代替上下文中的数据。
因为初次调用 SaveChanges,数据可以成功保存到数据库。但是在 ObjectContext 并未释放时,再次使用 SaveChanges 异步更新数据,就会引发OptimisticConcurrencyException 并发异常。当 RefreshMode 为 StoreWins 时,系统就会保留初次输入的数据属性。
此例子与上面的例子十分相似,只是把 RefreshMode 改为 StoreWins 而已。在业务逻辑较为复杂的的系统当中,建议使用此方式处理并发异常。在保留最初输入的数据修改属性后,把属性返还给客户,让客户进行对比后再决定下一步的处理方式。

观察测试结果,可见当 RefreshMode 状态为 StoreWins 时,系统将会以数据源中的数据代替上下文当中的对象属性。在业务逻辑较为复杂的的系统当中,建议使用此方式处理并发异常。


链接: https://pan.baidu.com/s/1gfu6fZl 密码: fyb3

练习的源码,有纠正的错误的朋友记得分享

转载于:https://www.cnblogs.com/taotaozujinet/p/7851875.html

EntityFramework 6 (EF6 DBcontext) 并发处理实战相关推荐

  1. 第二十三节: EF性能篇(三)之基于开源组件 Z.EntityFrameWork.Plus.EF6解决EF性能问题

    一. 开篇说明 EF的性能问题一直以来经常被人所吐槽,究其原因在于"复杂的操作在生成SQL阶段耗时长,且执行效率不高",但并不是没有办法解决,从EF本身举几个简单的优化例子: ①: ...

  2. EntityFramework中的DbContext使用疑点说明

    1.DbContext怎么在Asp.mvc中使用? public class Repository{//实例化EF容器:有弊端.一个线程里可能会创建多个DbContext//DbContext db ...

  3. 第一篇 Entity Framework Plus 之 Audit

    一般系统会有登陆日志,操作日志,异常日志,已经满足大部分的需求了.但是有时候,还是需要Audit 审计日志,审计日志,主要针对数据增,改,删操作数据变化的记录,主要是对数据变化的一个追踪过程.其中主要 ...

  4. 第二篇 Entity Framework Plus 之 Query Future

    从性能的角度出发,能够减少 增,删,改,查,跟数据库打交道次数,肯定是对性能会有所提升的(这里单纯是数据库部分). 今天主要怎样减少Entity Framework查询跟数据库打交道的次数,来提高查询 ...

  5. ORM系列之Entity FrameWork详解

    一. 谈情怀 从第一次接触开发到现在(2018年),大约有六年时间了,最初阶段连接数据库,使用的是[SQL语句+ADO.NET],那时候,什么存储过程.什么事务 统统不理解,生硬的将SQL语句传入SQ ...

  6. 【转】ORM系列之Entity FrameWork详解

    一. 谈情怀 从第一次接触开发到现在(2018年),大约有六年时间了,最初阶段连接数据库,使用的是[SQL语句+ADO.NET],那时候,什么存储过程.什么事务 统统不理解,生硬的将SQL语句传入SQ ...

  7. ASP.NET MVC 5 SmartCode Scaffolding for Visual Studio.Net

    介绍 ASP.NET MVC 5 SmartCode Scaffolding是集成在Visual Studio.Net开发工具中一个ASP.NET MVC Web应用程序代码生成框架,使用SmartC ...

  8. Autofac的使用

    Autofac的使用过程,以下为模板 配置: 1.Demo.Repository(1)NuGet包:①EntityFramework②Z.EntityFramework.Extensions③Z.Ex ...

  9. EF +MYSQL 配置

    <?xml version="1.0" encoding="utf-8"?> <!--有关如何配置 ASP.NET 应用程序的详细信息,请访问 ...

  10. 第二十一节:ADO层次上的海量数据处理方案(SqlBulkCopy类插入和更新)

    一. 简介 1. 背景: 虽然前面EF的扩展插件Z.EntityFramework.Extensions,性能很快,而且也很方便,但是该插件要收费,使用免费版本的话,需要定期更新,如果不更新,将失效, ...

最新文章

  1. html表格中加入斜线,在HTML中显示带斜线的表格
  2. 长方形与圆最近连线LISP_常见图形,圆形、长方形和正方形面积的计算
  3. 数据结构与算法JavaScript描述——链表
  4. inline hook学习
  5. activiti候选人的多个场景应用
  6. HDU - 6183 Color it(动态开点线段树/树状数组套动态开点线段树)
  7. 转:C++ map的基本操作和使用
  8. 项目按jar包方式部署
  9. 自然语言处理技术有哪些?NLP简介
  10. 03.整体管理+变更管理
  11. 金融零售图谱-反欺诈关联图谱
  12. 嵌入式硬件从接杜邦线起-杜邦头接线实操①
  13. 一篇几乎涵盖95%英语语法的文章[Pending]
  14. 【Linux】用最形象的例子学习进程,从入门到深入
  15. 5G中传和回传的承载解决方案
  16. 鸿蒙系统网络连接设置ip,网络ip地址错误解决方法
  17. SpringSSM的教师管理系统--ajax版本,分页,图片上传,jsp页面使用bootstrap布局
  18. CF - 794B. Cutting Carrot - 数学
  19. 好玩的Python-摩斯码发报机
  20. java向上转型_Java向上转型

热门文章

  1. mysql5.7 yum 密码,CentOS 7.7解决yum方式安装的MySQL 5.7 root用户密码丢失问题
  2. 【渝粤教育】国家开放大学2018年秋季 1374T班级管理 参考试题
  3. 【渝粤教育】电大中专液压与气动技术作业 题库
  4. 【渝粤教育】广东开放大学 供应链与物流管理 形成性考核 (56)
  5. 【渝粤题库】国家开放大学2021春2605经济法律基础题目
  6. LeetCode刷题系列(一)把回溯算法框架将给爷爷奶奶听
  7. Linux系统基础开发应用及Linux-C用户手册
  8. HDU 1084:What Is Your Grade?
  9. iOS微信运动 刷分
  10. Android Studio升级到3.0.0后构建项目时出现的问题总结