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. php中模糊查询并关联三个select框

    1.在php中我们经常用到下拉框,并相互关联,如果下拉框的option非常多,那么我们就要用到模糊搜索功能,那么怎么做呢? 在此功能中,走了弯路,最好不要关联两个select的id值后select属性 ...

  2. csharp:SQLite and Access using C# code read data

    SQLite sql script: CREATE TABLE BookKindList ( BookKindID INTEGER PRIMARY KEY AUTOINCREMENT, BookKin ...

  3. BZOJ2987:Earthquake(类欧几里德算法)

    Sol 设 \(n=\lfloor\frac{c}{a}\rfloor\) 问题转化为求 \[\sum_{i=0}^{n}\lfloor\frac{c-ax}{b}\rfloor+1=\sum_{i= ...

  4. Laravel基本使用

    laravel一.简介二.运行环境要求 1.php 版本>=5.5.9 2.Mcrypt PHP扩展 php的加密扩展,提供多种加密算法 3.openssl扩展 对传输的数据进行加密 4.mbs ...

  5. html开发那些不好的习惯,和问题。

    最近网上看了好多html开发中那些问题和不好的习惯,顺手总结一下. 一.上下间距 在开发中你会发现你明明设置的两个p标签上下间距为20px但你实际测量中会发现他会多4~8px,这是为什么呢!如果你是老 ...

  6. JS原生带小白点轮播图

    咱们刚刚说了js原生轮播图,现在给他加上可以随着一起走动的小圆点吧! css代码: *{ margin:0px; padding: 0px; } ul{ width: 2500px; height: ...

  7. [HttpException (0x80004005): 应用程序已预编译,因此不允许使用目录“/App_Code/”。]

    删除网站下的PrecompiledApp.config文件即可.

  8. 浏览器根对象window之Location

    1. Location Location 对象包含有关当前 URL 的信息.Location 对象是 Window 对象的一个部分,可通过 window.location 属性来访问. 1.1 Loc ...

  9. Android 录制视频

    Activity代码: package eoe.demo.Media; import java.io.File; import java.io.IOException; import android. ...

  10. 交叉编译 Cross-compiling for Linux

    @(134 - Linux) Part 1 交叉编译简介 1.1 What is cross-compiling? 对于没有做过嵌入式编程的人,可能不太理解交叉编译的概念,那么什么是交叉编译?它有什么 ...