如何处理Entity Framework / Entity Framework Core中的DbUpdateConcurrencyException异常(转载)
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异常(转载)的更多相关文章
- 如何处理Entity Framework中的DbUpdateConcurrencyException异常
1. Concurrency的作用 场景 有个修改用户的页面功能,我们有一条数据User, ID是1的这个User的年龄是20, 性别是female(数据库中的原始数据) 正确的该User的年龄是25 ...
- 你注意到 .Net Framework 和 .Net Core 中使用 Session 的区别了吗?
起因 在测试一个例子时发现的问题,这个示例实现的功能是刷新页面也能保持表格锁定列的状态,先看下页面的完成效果: 测试中发现,几乎相同的代码: 在 FineUIMvc(Net Framework)下没有 ...
- 在 ASP.NET CORE 中使用 SESSION (转载)
Session 是保存用户和 Web 应用的会话状态的一种方法,ASP.NET Core 提供了一个用于管理会话状态的中间件.在本文中我将会简单介绍一下 ASP.NET Core 中的 Session ...
- 如何处理C#的HttpWebResponse的GetResponse中的超时异常
程序中,有时会遇到超时的异常,需要进行处理,用一般的try...catch(Exception ex)...会发现ex没有status属性,此时使用WebException捕获异常: try { re ...
- 工厂参观记:.NET Core 中 HttpClientFactory 如何解决 HttpClient 臭名昭著的问题
在 .NET Framework 与 .NET Core 中 HttpClient 有个臭名昭著的问题,HttpClient 实现了 IDispose 接口,但当你 Dispose 它时,它不会立即关 ...
- 浅析Entity Framework Core中的并发处理
前言 Entity Framework Core 2.0更新也已经有一段时间了,园子里也有不少的文章.. 本文主要是浅析一下Entity Framework Core的并发处理方式. 1.常见的并发处 ...
- 如何在ASP.NET Core中应用Entity Framework
注:本文提到的代码示例下载地址> How to using Entity Framework DB first in ASP.NET Core 如何在ASP.NET Core中应用Entity ...
- ASP.NET Core 中的 ORM 之 Entity Framework
目录 EF Core 简介 使用 EF Core(Code First) EF Core 中的一些常用知识点 实体建模 实体关系 种子数据 并发管理 执行 SQL 语句和存储过程 延迟加载和预先加载 ...
- .net core Entity Framework 与 EF Core
重点讲 Entity Framework Core ! (一)Entity Framework 它是适用于.NET 的对象关系映射程序 (ORM),现在的EF6已经是久经沙场,并经历重重磨难,获得一致 ...
随机推荐
- HTML——基本html标签
基本html标签 <html> ... </html> 定义HTML文档 <head> ... </head> 文档的信息 <meta /&g ...
- ajax异步上传图片三种方案
转自:http://www.jb51.net/article/51180.htm 注:自己尝试了前两种,都可用: 目前常用的异步文件上传功能有几种,比较多见的如使用iframe框架形式,ajax功能效 ...
- osgEarth编译——以VS2012为例
整理记录下 osgEarth编译过程. osgEarth是依赖于OSG的三维地理平台. 准备工作 OpenSceneGraph-3.4.0.zip OSG_3RDPARTY_DIR http:/ ...
- Google 嘘! 嘘!
https://www.gufen.gq(无广告,原guso.ml,ggso.ga,guge.ga) https://c.aiguso.tk (无广告,体验良好) https://d.freedo.g ...
- python学习笔记之open函数的用法
先上一段代码 >>> f = open('1.txt','r'); >>> f.readline() #读取数据>>> f.close() #关闭 ...
- Windows Azure系列公开课 - 第二课:为什么选择Windows Azure(下)
Windows Azure中有什么? |基础结构 Windows Azure能够根据您企业的需求提供最适合的基础结构,并且在业务发展或精简时能够根据变化的需求及时的做出改变.您可以将Windows A ...
- leetcode Ch3-DFS & Backtracking I
一.树的遍历 [非递归版] 1. 后序 class Solution { public: vector<int> postorderTraversal(TreeNode *root) { ...
- Python学习---range/for/break/continue简单使用
range的使用:注意,在python3中,交互模式下已经不显示了 for循环的使用 打印50-70 # 第一种方案 for i in range(100): if i <= 70 and i ...
- Linux下php-fpm进程过多导致内存耗尽问题
这篇文章主要介绍了解决Linux下php-fpm进程过多导致内存耗尽问题,需要的朋友可以参考下 最近,发现个人博客的Linux服务器,数据库服务经常挂掉,导致需要重启,才能正常访问,极其恶心,于是 ...
- [BZOJ 2322][BeiJing2011]梦想封印
梦想封印 题意 原题面: Problem 2322. -- [BeiJing2011]梦想封印 2322: [BeiJing2011]梦想封印 Time Limit: 20 Sec Memory L ...