如何处理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已经是久经沙场,并经历重重磨难,获得一致 ...
随机推荐
- SQLite metadata
http://www.devart.com/dotconnect/sqlite/docs/MetaData.html https://github.com/sqlitebrowser/sqlitebr ...
- [APIO2018] Circle selection 选圆圈(假题解)
题面 自己去\(LOJ\)上找 Sol 直接排序然后\(KDTree\)查询 然后发现\(TLE\)了 然后把点旋转一下,就过了.. # include <bits/stdc++.h> # ...
- drupal的权限设置
通过hook_menu()设置url的权限,有两种方式: 方式一:定义函数,通过 access callback 'access callback' => 'fun()', function f ...
- NGINX本地服务器解析域名
1.找到hosts文件 ,添加需要解析的域名 2.在cmd命令窗口中检测解析是否生效 3 找到本地服务器的域名配置文件:添加绑定的域名,更改访问的目录 4.添加pathinfo.隐藏index.php ...
- Android Weekly Notes Issue #244
Android Weekly Issue #244 February 12th, 2017 Android Weekly Issue #244 本期内容包括: Android Fragments使用教 ...
- MUI框架-12-使用原生底部选项卡(凸出图标案例)
MUI框架-12-使用原生底部选项卡(凸出图标案例) 今天,用 mui 做 app 时,遇到了可能各位都遇到过的头疼问题:底部中间图标凸起,如下图: 最后有源代码 [提示]:有人问我在 HBuilde ...
- BubblePopupWindow
Android 实现各个方向的气泡弹窗,可控制气泡尖角偏移量. https://github.com/smuyyh/BubblePopupWindow 截图 使用: BubblePopupWindow ...
- 2 pygraphviz在windows10 64位下的安装问题(反斜杠的血案)
可以负责任的说,这篇文档是windows10安装pygraphviz中,在中文技术网站中最新的文档,没有之一.是自己完全结合各种问题,包括调试等,总结出来的. 问题来源:主要是可视化RvNN网络的树结 ...
- java笔记--正则表达式的运用(包括电话,邮箱验证等)
正则表达式 --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3877402.html "谢谢-- 正则表达式符号:" ...
- SQL Server ->> 高可用与灾难恢复(HADR)技术之 -- Transaction Replication(事务复制)
复制类型: 1)事务型复制:通过复制事务日志到订阅点重做的方式,属于增量型复制: 2)合并型复制:通过触发器和元数据表追踪表数据改变,同样属于增量型复制: 3)快照型复制:通过创建数据库快照,并把快照 ...