EF6学习笔记二十七:并发冲突(一)张四海

来到并发这里了,我自己得先承认,并发对我来说完全是一个熟悉又真正陌生的东西,总的来说,我对并发一无所知。

那么不管是怎么回事,我也要说一下。之前看过零星的一些讲硬件的东西,说的是,很多个应用你看似同时开启,同时运行的,其实只是,CPU速度太快,让你察觉不了。

所以不可能存在两个任务同时进行,这只是错觉。所以我现在给自己一些自信,我断定!不存在,就像一个啤酒瓶,口就那么大,一次只容许一颗珠子进去,不可能两个同时进去,都是错觉!

来看EF中的并发。

我们在使用EF上下文时,遵循的是一个请求对应一个上下文,对事务也是这个态度,不要事务那么长,越短越好。

一个请求对应一个上下文,那么服务器同时接受到了多个请求,构造出多个上下文对象,针对同一资源操作,问题就出来这里。

因为不同的上下文中查询出的实体都是各自的,并不是同一个引用。

这里有两个上下文,都得到了名叫“张三”的学生实体,第一个上下文修改为“李四”,第二个上下文修改为“张三”,那么最终的结果应该是“张三”,但是看看下面的代码,其实最终数据库的结果是“李四”

using(DB1_Contextctx1=newDB1_Context())using(DB1_Contextctx2=newDB1_Context()){varstu1=ctx1.Students.FirstOrDefault();varstu2=ctx2.Students.FirstOrDefault();stu1.Name="李四";stu2.Name="张三";ctx1.SaveChanges();ctx2.SaveChanges();}

你觉得应该是第一个上文查询修改完,再第二个上下文接着查询修改就行了。但是高并发的情况下是无法保证的。

那么我们看下一个上下文中查询相同的两个实体。引用是相等的。所以整个解决方案就使用一个上下文是不是就行了?我觉得是,但是这是不科学的。

using(DB1_Contextctx=newDB1_Context()){varstu1=ctx.Students.FirstOrDefault();varstu2=ctx.Students.FirstOrDefault();Console.WriteLine(ReferenceEquals(stu1,stu2));//TrueConsole.WriteLine($"stu1.Name:{stu1.Name},stu2.Name:{stu2.Name}");//stu1.Name:小新77,stu2.Name:小新77}并发冲突做个分初级、中级和高级来讲,我这篇笔记主要记录初级内容的学习心得。

现在来认识一下悲观并发和乐观并发,这是两种并发的控制方法

悲观并发:当更新特定记录时,同一记录上的所有其他并发更新将被阻塞,直到当前操作完成或者放弃,其他并发操作才可以继续。

乐观并发:当更新特定记录时,同一记录上的所有其他并发将导致最后一条记录被保存(获胜)。假设由于并发访问共享资源而导致资源冲突并不是不可能过的,而是不可用的,此时将采取一定手段来解决并发冲突。

上面的张三李四就是属于乐观并发,就是我就随他去了,它自己修改到哪里就是哪里,我也不关心过程。

那么如何解决上面的问题,上面是什么问题?就是我第二个上下文查询出实体不是最新的,应该将这种情况看做是一种异常,但是如果你用Try/catch来捕获是捕获不到的。

因为捕获并发冲突需要特殊配置,EF就为我们提供了两种方式:并发Token、行版本(RowVersion)

如果我们对student的Name属性这是并发Token,需要将属性进行如下配置

modelBuilder.Entity().Property(x=>x.Name).IsConcurrencyToken();

现在来用try/catch就可以捕获了

这个异常我之前没有学到这里来的时候碰到过,没有记录下来当时是写的什么代码,真可惜!

来看看行版本的方式。这就需要为实体添加一个字节数组类型的属性,并且该属性需要配置

那么接下来我们就开始在异常处理中进行操作,他不是数据不是最新的吗?那么我就让他得到最新的。因为EF中有针对并发异常的类(DbUpdateConcurrencyException)。

DbUpdateConcurrencyException中具有Entries属性,该属性返回一系列DbEntityEntry对象,表示冲突实体的跟踪信息。

using(DB1_Contextctx1=newDB1_Context())using(DB1_Contextctx2=newDB1_Context()){varstu1=ctx1.Students.FirstOrDefault();varstu2=ctx2.Students.FirstOrDefault();stu1.Name="小新111";ctx1.SaveChanges();stu2.Name="小新222";try{ctx2.SaveChanges();}catch(DbUpdateConcurrencyExceptionex){vars=ex.Entries.Single();s.Reload();Console.WriteLine("stu2.Name:"+stu2.Name);//小新11stu2.Name="小新222";ctx2.SaveChanges();throwex;}}

调用Reload方法来刷新数据库中的最新值到当前内存中的值,就是造成并发冲突的这个对象,更新它。

如果说不用Relod,也有另外一种方式来实现

这里有一个疑问,照我的理解应该是将current的值赋值给当前数据库中的值,也就是tracking.GetDatabaseValues().SetValues(current);

但是这样写报错,虽然作者也专门解释了,但是我还是懵的……

行吧,这个还是必要自己去动手弄一下,体会一下。初级版的并发冲突解决方案就到这里了。

后面还是不得不说一下,我也是今天才知道多个using可以这个很简单的堆叠起来写,很优雅啊。

然后利用上下文的日志打印真的很有用。

using(DB1_Contextctx1=newDB1_Context())using(DB1_Contextctx2=newDB1_Context()){ctx1.Database.Log=msg=>Console.WriteLine("ctx111111111111111:"+msg);ctx2.Database.Log=msg=>Console.WriteLine("ctx222222222222222:"+msg);varstu1=ctx1.Students.FirstOrDefault();varstu2=ctx2.Students.FirstOrDefault();stu1.Name="小新11";stu2.Name="小新22";ctx1.SaveChanges();ctx2.SaveChanges();}

从打印的结果可以看到,关于数据库初始化的任务全部是由ctx1去执行的,就是这些什么Migration这些东西

难道是我ctx1对象先构造的问题?或者ctx1的log先打印的问题,于是我改成ctx2先构造,然后ctx2的log也先执行,发现还是上面打印的结果,还是ctx1去执行数据库初始化的工作。

直到我将ctx2先查询出student对象才变成ctx2先执行这些操作。所以是不是就认识到,多个上下文到底是谁来负责数据库初始化的任务呢?那就看看是谁先与数据库交互了,现在构造上下文对象这里并没有与数据库发生交互。

THE END
1.茂南区市场物业管理中心2024年政府信息公开工作年度报告本报告按照《中华人民共和国政府信息公开条例》(以下简称《条例》) 及《国务院办公厅政府信息与政务公开办公室关于政府信息公开工作年度报告有关事项的通知》(国办公开办函[2019]60号)、《茂名市茂南区人民政府办公室关于印发茂名市茂南区贯彻落实2019年政府公开工作要点分工方案的通知》(茂南府办函[2019]36号)等要求http://www.maonan.gov.cn/zwgk/bmzjxxgkgzndbg/content/post_1431902.html
2.贵州中公教育各分校联系方式联系方式贵州教师考试网网站导航 备考资料 手机APP 用户服务 400-6300-999 贵州中公教育 地址:贵阳市云岩区延安东路230号贵盐大厦8楼(荣和酒店楼上) 电话:0851-85805808、400-6300-999 网址:http://gz.zgjsks.com/ 路线:乘坐10 、11、12、17、61、253、252、262、26、28、3、4路等到师大站下车走100米即到 http://gz.zgjsks.com/html/contactus/
3.关于干部任前公示的公告问题龙男现将拟提拔使用的李龙同志有关情况予以公示,接受广大干部群众监督。如发现在德、能、勤、绩、廉等方面有影响任职的问题,可通过电话、来信、面谈等形式反映,我们将认真受理,并向署名或当面反映问题的干部群众反馈调查核实结果。如不存在影响任职的问题,将按有关规定办理任职手续。 https://gov.sohu.com/a/846477571_120684009
4.恩科(苏州)通风系统有限公司CN103511353A发明公布2014-01-15圆形管道风机的改良结构液体变容式机械;液体泵或弹性流体泵孙本启 CN202646163U实用新型2013-01-02圆形管道风机的改良结构液体变容式机械;液体泵或弹性流体泵孙本启 CN203394203U实用新型2014-01-15全热交换器箱体检修门用搭扣式开关锁;钥匙;门窗零件;保险箱孙本启 https://www.11467.com/suzhou/co/313594.htm
5.高雅足道(汪洲桥店)电话,地址,价格,营业时间(图)3053 条评价 人均: 106 元技师: 7.4环境: 7.4服务: 7.4 地址: 仙桃大道35-2号 电话:0728-32421** 特色: 更多信息 写评价 网友评价(3053) 全部图片(146)好评(2749)中评(161)差评(143) 肖霞254 两根棉棒还收10块钱,要另外收费起码要提前说一下吧,搞得出去叫买单,真是尴尬,不好的一次体验,采耳也是很马https://www.dianping.com/shop/10351135
6.好玩的小方块游戏有哪些2024趣味足的方块游戏合集《小方块》在游戏里大小不一的方块会同时向屏幕下方掉落,玩家要找到其中的小方块,并且将色彩一致的小方块进行消除,为玩家加入了令人成瘾的挑战模式,以及上百个令人振奋的游戏关卡,而这些关卡有着众多奇思妙想的设定,比如玩家消除足够多的方块数量后,能够触发激活道具,这些道具都隐藏在大方块中间,有时间冻结、地雷等道具https://www.9game.cn/news/10351135.html
7.发展期的白癜风怎么治好呢快速问医生如果发现自己患有白癜风疾病,那么就要尽快的采取治疗措施,方法有很多,一般会采取中西医结合的方式来进行治疗,白癜风的患者还要按照自己的实际情况来选择合适的治疗措施,所以说患者一定要到专科医院做一个全面的检查,医生会根据患者自身的身体状况,来采取合理的用药,特别是皮质固醇类药物的使用,病情是有一定的控制效果的。https://m.120ask.com/yiyuan/news/10351135.html
8.乙肝疫苗补种第二针与第三针间隔应大于几周39问医生乙肝疫苗补种第二针与第三针间隔应大于6个月。乙肝疫苗是一种预防乙型肝炎病毒感染的疫苗,接种后可以刺激机体产生抗体,从而起到预防乙型肝炎病毒感染的作用。 乙肝疫苗补种第二针与第三针间隔通常为6个月,乙肝疫苗全程接种一共需要3针,第二针和第三针间隔通常为1个月,所以乙肝疫苗补种第二针与第三针间隔应大于6https://wapask-mip.39.net/mip/question/103511356.html
9.大概四五年前被野生鼹鼠咬了流了有问必答问题描述:(女 , 20岁)大概四五年前被野生鼹鼠咬了流了很多血,当时消毒了,但没注射预防针,目前也并没有什么症状,不知道会不会有什么问题呢 医生回答(1) 贺世豪 副主任医师 江陵县人民医院 问题分析:没有问题了,鼠类动物有可能拾病原体,如狂犬病毒,巴通体、钩端螺旋体,破伤风等,但会有一定的潜伏期。意见https://mip.club.xywy.com/mip/20160316/103511353.htm
10.虾肉有没有营养?问答频道问题描述:(女,26岁)每次朋友喊吃虾的时候,我都不太想去,因为觉得吃虾很麻烦,而且感觉虾也不是很健康,虾肉有没有营养? 医生回答(1) 胡小翠 主任医师 湖南省肿瘤医院 指导意见:虾肉的营养元素很丰富,虾是我们经常可以吃到的美食,不但滋味鲜美,而且口感滑嫩、营养非常丰富!虾肉中富含肌球蛋白,蛋白纤维很短,https://m.bohe.cn/ask/mip/103511356.html
11.最近几天气管痒痒需要如何解决全科气管痒的现象考虑是由于支气管炎引起的症状,也有可能是由于过敏因素导致的症状,建议你应该注意平时的饮食习惯,而且避免吸烟饮酒,也不要吸入一些刺激性的气体,避免导致症状加重。应该多喝水。为确定原因,最好到正规医院做检查,进行针对性的治疗。 回答于2023-11-26 03:11 相关https://m.fh21.com.cn/iask/dzmip/103511355.html