1. Concurrency的作用


场景
有个修改用户的页面功能,我们有一条数据User, ID是1的这个User的年龄是20, 性别是female(数据库中的原始数据)
正确的该User的年龄是25, 性别是male
 
这个时候A发现User的年龄不对, 就给改成25, 那么在Entity Framework中,我们会这样做。

var user = dbConext.User.Find();

//B用户在这里完成修改了User的性别

user.age = ;

dbContext.SaveChanges();

但是假如在上面注释处,有个B用户发现性别不对,完成了对用户性别的修改,改成male. 会出现什么结果呢。
var user = dbConext.User.Find(1);
当A执行这个代码的时候,获取的性别是female
user.age = 25;
当A执行这个代码的时候, 不知道B已经修改了这个记录的性别,这个时候A的user的性别还是female
dbContext.SaveChanges();
保存修改的时候,会把female覆盖回去,这样B用户的修改就作废了。
 
但这不是A的本意,A其实只是想修改年龄而已。
Entity Framework使用[ConcurrencyCheck] 来解决这种问题, 当标记为[ConcurrencyCheck] 的Entity属性,如果发现在从数据库中取下来和提交的时候不一致,就会出现DbUpdateConcurrencyException异常,避免错误提交。

顺便说下,如果在实体类的属性上不用[ConcurrencyCheck]标签,在EF Core中上面这种情况是不会抛出异常的。不加[ConcurrencyCheck]标签时,EF Core中发生DbUpdateConcurrencyException异常的条件是,使用DbContext.SaveChanges方法时,生成的Update或Delete语句根据实体的Key属性值在数据库表中找不到对应行。

不过实测EF Core的实体类属性加上[ConcurrencyCheck]标签后,也会出现上面说的问题。

2. 如何正确处理DbUpdateConcurrencyException异常


2.1 放弃更新或放弃删除数据库中已经不存在的数据

原理就是,发生DbUpdateConcurrencyException异常时将实体的EntityState设置为Detached,放弃更新或放弃删除抛出DbUpdateConcurrencyException异常的实体,只更新或删除不抛出异常的实体:

using (TestDBContext dbContext = new TestDBContext())
{
var jim = dbContext.Person.Where(p => p.Name == "Jim").First();
var sam = dbContext.Person.Where(p => p.Name == "Sam").First();
var jack = dbContext.Person.Where(p => p.Name == "Jack").First(); jim.Age = jim.Age + ;
sam.Age = sam.Age + ;
jack.Age = jack.Age + ; bool saveFailed;
do
{
saveFailed = false;
try
{
dbContext.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// 获取抛出DbUpdateConcurrencyException异常的实体
var entry = ex.Entries.Single(); // 设置实体的EntityState为Detached,放弃更新或放弃删除抛出异常的实体
entry.State = EntityState.Detached; }
} while (saveFailed);
}

也可以重写DbContext的SaveChanges方法,加入上面的逻辑来安全使用DbContext.SaveChanges方法,当Entity Framework的实体类属性上不用[ConcurrencyCheck]标签时,这种方式就很适合(也就是说,使用DbContext.SaveChanges方法时,如果生成的Update或Delete语句根据实体的Key属性值在数据库表中找不到对应行,从而抛出了DbUpdateConcurrencyException异常,这种方式就能很好地进行处理,放弃更新或放弃删除抛出异常的实体):

public override int SaveChanges()
{
bool saveFailed;
do
{
saveFailed = false;
try
{
return base.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// 获取抛出DbUpdateConcurrencyException异常的实体
var entry = ex.Entries.Single(); // 设置实体的EntityState为Detached,放弃更新或放弃删除抛出异常的实体
entry.State = EntityState.Detached; }
} while (saveFailed); return ;
}

2.2 数据库优先方式

原理是在出现异常的时候,重新加载数据库中的数据,覆盖DbContext本地数据

using (TestDBContext dbContext = new TestDBContext())
{
var jim = dbContext.Person.Where(p => p.Name == "Jim").First();
var sam = dbContext.Person.Where(p => p.Name == "Sam").First();
var jack = dbContext.Person.Where(p => p.Name == "Jack").First(); jim.Age = jim.Age + ;
sam.Age = sam.Age + ;
jack.Age = jack.Age + ; bool saveFailed;
do
{
saveFailed = false;
try
{
dbContext.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Update the values of the entity that failed to save from the store
ex.Entries.Single().Reload();
}
} while (saveFailed);
}

2.3 客户端优先方式

以DbContext保存的客户端数据为主,覆盖数据库中的数据

using (TestDBContext dbContext = new TestDBContext())
{
var jim = dbContext.Person.Where(p => p.Name == "Jim").First();
var sam = dbContext.Person.Where(p => p.Name == "Sam").First();
var jack = dbContext.Person.Where(p => p.Name == "Jack").First(); jim.Age = jim.Age + ;
sam.Age = sam.Age + ;
jack.Age = jack.Age + ; bool saveFailed;
do
{
saveFailed = false;
try
{
dbContext.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Update original values from the database
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveFailed);
}

2.4 根据情况判断做相应的处理
综合前面几种方式的处理方法,根据抛出DbUpdateConcurrencyException异常实体的EntityState值做相应的处理

using (TestDBContext dbContext = new TestDBContext())
{
var jim = dbContext.Person.Where(p => p.Name == "Jim").First();
var sam = dbContext.Person.Where(p => p.Name == "Sam").First();
var jack = dbContext.Person.Where(p => p.Name == "Jack").First(); jim.Age = jim.Age + ;
sam.Age = sam.Age + ;
jack.Age = jack.Age + ; bool saveFailed;
do
{
saveFailed = false;
try
{
dbContext.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
var entry = ex.Entries.Single();
//The MSDN examples use Single so I think there will be only one
//but if you prefer - do it for all entries
//foreach(var entry in ex.Entries)
//{
if (entry.State == EntityState.Deleted)
//When EF deletes an item its state is set to Detached
//http://msdn.microsoft.com/en-us/data/jj592676.aspx
entry.State = EntityState.Detached;
else
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
//throw; //You may prefer not to resolve when updating
//}
}
} while (saveFailed);
}

不过这种方式可能会造成死循环。。。还需要改进,原因如下:

well according to msdn.microsoft.com/en-us/library/… ex.Entries contains all the entities that could not be saved. The only problem i have with the while loop is that it will spin forever if there is an entity that could not be saved for a reason other than entry.State == EntityState.Deleted. 

来自链接

参考文献:

如何处理Entity Framework中的DbUpdateConcurrencyException异常

如何处理Entity Framework / Entity Framework Core中的DbUpdateConcurrencyException异常(转载)的更多相关文章

  1. 如何处理Entity Framework中的DbUpdateConcurrencyException异常

    1. Concurrency的作用 场景 有个修改用户的页面功能,我们有一条数据User, ID是1的这个User的年龄是20, 性别是female(数据库中的原始数据) 正确的该User的年龄是25 ...

  2. 你注意到 .Net Framework 和 .Net Core 中使用 Session 的区别了吗?

    起因 在测试一个例子时发现的问题,这个示例实现的功能是刷新页面也能保持表格锁定列的状态,先看下页面的完成效果: 测试中发现,几乎相同的代码: 在 FineUIMvc(Net Framework)下没有 ...

  3. 在 ASP.NET CORE 中使用 SESSION (转载)

    Session 是保存用户和 Web 应用的会话状态的一种方法,ASP.NET Core 提供了一个用于管理会话状态的中间件.在本文中我将会简单介绍一下 ASP.NET Core 中的 Session ...

  4. 如何处理C#的HttpWebResponse的GetResponse中的超时异常

    程序中,有时会遇到超时的异常,需要进行处理,用一般的try...catch(Exception ex)...会发现ex没有status属性,此时使用WebException捕获异常: try { re ...

  5. 工厂参观记:.NET Core 中 HttpClientFactory 如何解决 HttpClient 臭名昭著的问题

    在 .NET Framework 与 .NET Core 中 HttpClient 有个臭名昭著的问题,HttpClient 实现了 IDispose 接口,但当你 Dispose 它时,它不会立即关 ...

  6. 浅析Entity Framework Core中的并发处理

    前言 Entity Framework Core 2.0更新也已经有一段时间了,园子里也有不少的文章.. 本文主要是浅析一下Entity Framework Core的并发处理方式. 1.常见的并发处 ...

  7. 如何在ASP.NET Core中应用Entity Framework

    注:本文提到的代码示例下载地址> How to using Entity Framework DB first in ASP.NET Core 如何在ASP.NET Core中应用Entity ...

  8. ASP.NET Core 中的 ORM 之 Entity Framework

    目录 EF Core 简介 使用 EF Core(Code First) EF Core 中的一些常用知识点 实体建模 实体关系 种子数据 并发管理 执行 SQL 语句和存储过程 延迟加载和预先加载 ...

  9. .net core Entity Framework 与 EF Core

    重点讲 Entity Framework Core ! (一)Entity Framework 它是适用于.NET 的对象关系映射程序 (ORM),现在的EF6已经是久经沙场,并经历重重磨难,获得一致 ...

随机推荐

  1. OI中的一些模板

    线性筛 #include"cstdio" #include"cstring" #include"iostream" #include&quo ...

  2. 001profile条件化创建bean

    01.类级别条件创建 @Configuration @Profile("dev") public class Aclass{}---->影响整个类,包括类的注解.开发环境,类 ...

  3. Esri大数据分析引擎GeoAnalytics Server部署经历

    系统架构 Base WebGIS 4Cores 16GB Spatiotemporal Data Store 32GB SSD Disk 足够大的空间 GA Server 4Cores 16GB 足够 ...

  4. 根据HTML5 获取当前位置的经纬度【百度地图】【高德地图】

    是想让地图的定位用户位置更准确一些. 查看了介绍: http://www.w3school.com.cn/html5/html_5_geolocation.asp 看介绍中拿数据挺简单. <!D ...

  5. C# 后台添加Log信息

    我们在做项目的时候,经常会使用到Log日志,今天分享一下如何在后台添加Log信息 创建一个写Log的方法: public void WriteLog(string Action) { try { st ...

  6. A memory map of an object

    Phone类 package com.itheima_02; /* * 手机类: * 成员变量:品牌,价格,颜色... * 成员方法:打电话,发短信... */ public class Phone ...

  7. JS字面量创建方式的优缺点

    http://www.cnblogs.com/wuyaxing/p/6416441.html

  8. firewall 如何开放端口

    转自官方网页:http://www.firewalld.org/documentation/howto/open-a-port-or-service.html How to open port 80/ ...

  9. PHP中的trait方法冲突

    laravel使用中我们可能需要对User model使用softdelete这个功能,以便删除后还可以恢复,不幸的是entrust模块也有这个方法,两者产生冲突, 解决办法: https://lar ...

  10. CentOS随笔 - 修改CentOS7的IP

    前言 转帖请注明出处: http://www.cnblogs.com/Troy-Lv5/ 在使用CentOS时经常我们需要固定一个IP, 因为服务器嘛,不固定IP. 难道每次都要配置开发环境的地址.? ...