处理 EF 并发其实就这么简单

摘要:
系统会提前定义数据并发导致的并发异常处理模式:通常有三种处理方法:a保留上次对象修改的值,b保留原始修改值,c合并修改后的值。这三种处理方法将在以下章节中逐一描述。仅允许一个用户同时修改同一数据。

    最近项目有点闲,终于可以了解点自己想了解的了,以前听同事讲面试的经历总会被问到“如何处理高并发大数据” 乍一听感觉这东西好像很有学问的样子,于是并发这个词在脑海里留深刻印像,而且在自己心中的技术地位也提高很多,也导致了解并发相关的知识时,也带着思想负担,总以为很难懂,程序员或许都是这样,在自己不懂的技术领域,别人说一个很简单的技术,给他的感觉都是很高深的样子,其实自己一了解就会发现,“哎哟 我 C 原来就这样儿啊!”总之只要你想了解,花点时间就没有难的事! 好了 正式进入正题

EF并发处理 在此之前先把与并发相关的一此知识点给大家介绍一下,有些虽然不是了解并发的必要条件,但我认为多了解一点又有什么不好呢?

一 EF并发介绍

 

    什么叫并发:当多个用户同时更新同一数据的时候,由于更新可能导致数据的不一致性,使得程序的业务数据发生错误,这种情况可以称之为并发。

并发又分为两种:乐观并发 与 悲观并发 

  乐观并发即系统允许多个用户同时修改同一条记录,系统会预先定义由数据并发所引起的并发异常处理模式,去处理修改后可能发生的冲突

当出现乐观并发时应该怎么处理呢,通常有如下三种处理方法 

     a 保留最后一次对象修改的值

   b 保留最初的修改值

   c  合并修改值

这三种处理方法下文会一一介绍

     悲观并发:在同一时刻只允许一个用户修改相同数据,直接用Lock 与 unLock就可以处理,后文就不再解释了 

二  EF对象状态

 

在EF中所有的对象状态只有被添加到ObjectContext 上下文中才能被跟踪,才能进行持久化操作,那么在ObjectContext中对于对象状态分几种呢?有如下五种

Detached:对象存在,但未由对象服务跟踪。在创建实体之后、但将其添加到对象上下文之前,该实体处于此状态
Unchanged: 自对象加载到上下文中后,或自上次调用 System.Data.Objects.ObjectContext.SaveChanges() 方法后,此对象尚未经过修改
Added: 对象已添加到对象上下文,但尚未调用 System.Data.Objects.ObjectContext.SaveChanges() 方法
Deleted:使用 System.Data.Objects.ObjectContext.DeleteObject(System.Object) 方法从对象上下文中删除了对象
Modified:对象已更改,但尚未调用 System.Data.Objects.ObjectContext.SaveChanges() 方法
这些状态 在了解并发的原理时会用到
 

三  EF事务隔离级别

在这里只例举三个最常用到的隔离级别,其它的有待朋友们自己行研究了

ReadCommitted:不可以在事务期间读取可变数据,但是可以修改它(EF 默认的隔级别)

ReadUncommitted:可以在事务期间读取和修改可变数据。

Serializable:可以在事务期间读取可变数据,但是不可以修改,也不可以添加任何新数据。

随着隔离级别的提高,可以更有效地防止数据的不一致性。但是,这将降低事务的并发处理能力,会影响多用户访问

四 事务不同隔离级别带来数据读取的不同结果

脏读:当一个事务读取数据修改后以经SaveChange但事务还没有提交,此时另外一个事务就读取了该数据,此时的数据就是脏数据,这一过程就是脏读

不可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的这一过程就是不可重复读

幻读:一个事务针对一张表的所有数据进行读取修改,而此时另一个事务向表中插入了一条数据,则第一个事务数据不包含新数据,像出现幻觉一样,这一过程就是幻读

ReadCommitted 会引发 不可重复读 和幻读

ReadUncommitted 会引发 脏读 ,不可重复读 和幻读

Serializable 以上三种都不会发生 

可见 Serializable的隔离级别是最高的,数据也是最准确的,但是高正确率也是要付出代价的,在此种隔离级别下,读取数据,与更新数据的效率也是最低的

五 EF并发的处理流程

    对于并发原理是这样的,每一次操作表 字段TimeStamp的值都会改变,而每次对表做操作时都会比对对象的TimeStamp值与数据库中表的值是否相同,是则操作表,否 ,则抛出异常给客户端或是刷新对象状态后重新保存,详细步骤如下

  1 在表中新建一个字段类型为TimeStamp , TimeStamp类型在.netFrameWork中是一个8位的数组,在SQL中则是一串二进制字符

  2 在.edmx 对象实体模型图中,右键TimeStamp字段 属性-->并发模式-->选择Fixed

      处理 EF 并发其实就这么简单第1张处理 EF 并发其实就这么简单第2张

     注意:把并发模式 设为Fixed后每一次操作表都会把TimeStamp字段当做条件查询,只有相等才能成功,以下是用SQL Profile跟踪到的结果 在 where 处可以看到效果

     处理 EF 并发其实就这么简单第3张

     3 模拟并发,通过捕获异常在异常处返回消息给客户端 异常类型有如下两种 

       3.1 EF 自定义异常:System.Data.Entity.Infrastructure.DbUpdateConcurrencyException 
       3.2 .net FrameWorkn异常:System.Data.OptimisticConcurrencyException
 
     4 并发处理完毕
 
具体代码如下
 1             //第一次加载对象更新后暂不保存到数据库
 2             var fContext = new BolgModelEntities();
 3             var menuObj = fContext.Menu.FirstOrDefault();
 4             menuObj.MenuName = "C#";
 5 
 6             using (var sContext = new BolgModelEntities())
 7             {
 8                 //第二次加载对象更新后,保存到数据库 此时TimeStamp的值已改变,与menuObj对象的TimeStamp值已不同,所以在menuObj保存时会抛异常出来
 9                 var obj = sContext.Menu.FirstOrDefault();
10                 obj.MenuName = "WPF";
11                 sContext.SaveChanges();//可以顺利保存
12             }
13 
14             try
15             {
16                 //保存会抛异常,因为TimeStamp 值不匹配
17                 fContext.SaveChanges();
18             }
19             //catch (DbUpdateConcurrencyException ex) //EF 自定义异常
20             //{
21                 
22             //    fContext.Refresh(RefreshMode.ClientWins, menuObj);
23             //    fContext.SaveChanges();
24             //}
25             catch (System.Data.OptimisticConcurrencyException ex) //.net FreamWork 定义异常
26             {
27                 //捕获异常后依然对数据进行保存
28                 fContext.Refresh(RefreshMode.ClientWins, menuObj); // RefreshMode.ClientWins 保存对象更新后的值 且对象的状态为Modified。
29 
30                 fContext.Refresh(RefreshMode.StoreWins, menuObj);//RefreshMode.StoreWins 保存数据库中原有值, 且对象的状态为Modified
31 
32                 fContext.SaveChanges();//SaveChanges完成后对象状态变为Unchanged
33                 
34             }
35             catch (System.Data.OptimisticConcurrencyException ex)
36             {
37                 //捕获异常后不再处理,将消息返回给客户端
38                 string message = string.Empty;
39                 message = "出现数据冲突请重新提交";
40                 return message;
41             }

在并发抛出异常后可以根据业务需,向客户端返回消息,

也可以直接处理冲突后的数据

     a 保留最后一次对象修改的值 用 RefreshMode.ClientWins

   b 保留最初的修改值   用 RefreshMode.StoreWins

   c  合并修改值  针对同对象不同属性一样可以 用 RefreshMode.StoreWins

好了到此 关于EF的并发就写完了,当然我只是把并发的基础说了一下,对于更高效,科学的并发,还需要朋友根据自己项目 的情况来做相应的处理

其实 写完了才发现关于EF的并发 其实并没有想象中的那么麻烦,只要多花点时间,多看看资料,问题就不大了

对于以上内容,如有不对之处,还望各位能指出,不要让我误导了他人,

另外如果觉得,本文对你有那么一点帮助,还望不吝啬的点一点 推荐您的推荐将是我源源不断的写作力

免责声明:文章转载自《处理 EF 并发其实就这么简单》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇listbox美化prometheus SQL汇总下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

如何退出正在Sleep的线程

    今天有个同事问我Thread的Interrupe方法,这个方法用于终止另一个正在等待(Sleep/Wait/Join)状态的线程,如果那个线程未处于等待状态,则等到下次进入等待状态时再抛出。     这个方法的平时用的机会其实并不大,由于需要线程处于等待状态,很大程度上限制了使用的机会,因此问了下同事实际的使用场景,原来是某些线程进入了长时间的Sl...

ICP算法使用遇到的问题

这几天在学习数据关联的方法,本来想使用ICP算法进行距离测距数据的配准,但是用的过程中出现问题,配的不准,而且偏差更大了。 红色的和黄色的2维激光点进行ICP配准,但将变换矩阵和黄色进行乘之后偏差更大了。怀疑是因为两个点集只有部分数据重合,而ICP算法最好是点能一一对应。 之后使用PCL进行点集匹配测试,出现同样的问题。 于是我自己构造了一个数据,将A...

抓取Web网页数据分析

通过程序自动的读取其它网站网页显示的信息,类似于爬虫程序。比方说我们有一个系统,要提取BaiDu网站上歌曲搜索排名。分析系统在根据得到的数据进行数据分析。为业务提供参考数据。   为了完成以上的需求,我们就需要模拟浏览器浏览网页,得到页面的数据在进行分析,最后把分析的结构,即整理好的数据写入数据库。那么我们的思路就是:   1、发送HttpRequest请...

poi excel给单元格增加批注(包含SXSSF)及设置列类型

import org.apache.poi.xssf.usermodel.*; import java.io.FileOutputStream; import java.io.IOException; public class PoiAddComments { public static void main(String[] args) thr...

第七章 mysql 事务索引以及触发器,视图等等,很重要又难一点点的部分

【索引】 帮助快速查询 MyISAM ,InnoDB支持btree索引 Memory 支持 btree和hash索引 存储引擎支持 每个表至少16个索引   总索引长度至少256字节   创建索引的优点: 1, 加快查询速度 2, 创建唯一索引来保证数据表中数据的唯一性 3,  实现数据的完整性 4, 实现数据的完整性,,加速表和表之间的连接, 5, 减少...

UNIX网络编程——TCP/IP简介

一、ISO/OSI参考模型        OSI(open system interconnection)开放系统互联模型是由ISO(International Organization for Standardization)国际标准化组织定义的网络分层模型,共七层,如下图:                                      物理...