[英]How to fix OptimisticConcurrencyException in an MVC application
我们有一个应用程序给出以下错误消息(不时,不经常):
System.Data.Entity.Core.OptimisticConcurrencyException:存储更新,插入或删除语句影响了意外的行数(0)。 自加载实体以来,实体可能已被修改或删除。 刷新ObjectStateManager条目
我查看了其他答案并进行了尝试(主键ID为零),但我无法重现。
保存记录的代码被实现为命令调用程序。 Execute方法接收要保存的命令对象,并调用处理程序的Handle方法:
public class CommandInvoker : ICommandInvoker
{
private readonly IKernel _kernel;
private readonly IArhomaContext _context;
public CommandInvoker(IKernel kernel, IArhomaContext context)
{
_kernel = kernel;
_context = context;
}
public void Execute<T>(T command)
{
var handler = _kernel.TryGet<ICommandHandler<T>>();
handler.Handle(command);
_context.SaveChanges();
}
}
有人可以指导我如何解决此问题吗? 我们是否应该将SaveChanges放入try catch并捕获OptimisticConcurrencyException? 我无法复制它,但是我们可以在生产中使用它。
在解决之前,让我们先了解一下正在发生的事情。 乐观并发意味着您在获取记录之前假设没有其他人会更改它。 悲观并发是指您锁定记录时,确保没有其他人可以更改任何内容。 您很少在断开连接的应用程序(例如网站)中使用悲观情绪。 而是,您捕获错误并确定下一步要做什么。
如果发现错误,您有很多选择。 首先是要警告用户当前数据库中的内容以及他们将要保存的内容并让他们决定。 当您再次获取记录时,可以将其保存回去(如果在保存之前有人再次更改(非常罕见?),则将其覆盖)。 第二个是简单地改写,假设最终获胜。 您还可以使用一种方法,其中根据发生的变化使用不同的方法,例如:
用户A(您的用户)希望在非规范化记录上更改用户B更改地址的电话号码。 您可以在后台将两者结合起来并保存。
使用EF,最简单的方法是重新刷新记录并覆盖字段,然后保存,因为这样您将获得一个上下文被数据库识别为未更改的副本。 您添加多少“安全性”或添加用户交互是您的选择,并且取决于业务需求。
这是一篇有关在MVC应用程序中使用EF处理并发性的出色文章。
本文出于所有目的和目的显示了单层应用程序的示例。 但是,它很好地说明了该方法。 它基本上捕获DbUpdateConcurrencyException
并向用户显示原始值,使他们能够确定是否继续其操作。 它还提供了RetryLimitExceededException
处理。
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(int? id, byte[] rowVersion)
{
string[] fieldsToBind = new string[] { "Name", "Budget", "StartDate", "InstructorID", "RowVersion" };
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var departmentToUpdate = await db.Departments.FindAsync(id);
if (departmentToUpdate == null)
{
Department deletedDepartment = new Department();
TryUpdateModel(deletedDepartment, fieldsToBind);
ModelState.AddModelError(string.Empty,
"Unable to save changes. The department was deleted by another user.");
ViewBag.InstructorID = new SelectList(db.Instructors, "ID", "FullName", deletedDepartment.InstructorID);
return View(deletedDepartment);
}
if (TryUpdateModel(departmentToUpdate, fieldsToBind))
{
try
{
db.Entry(departmentToUpdate).OriginalValues["RowVersion"] = rowVersion;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var clientValues = (Department)entry.Entity;
var databaseEntry = entry.GetDatabaseValues();
if (databaseEntry == null)
{
ModelState.AddModelError(string.Empty,
"Unable to save changes. The department was deleted by another user.");
}
else
{
var databaseValues = (Department)databaseEntry.ToObject();
if (databaseValues.Name != clientValues.Name)
ModelState.AddModelError("Name", "Current value: "
+ databaseValues.Name);
if (databaseValues.Budget != clientValues.Budget)
ModelState.AddModelError("Budget", "Current value: "
+ String.Format("{0:c}", databaseValues.Budget));
if (databaseValues.StartDate != clientValues.StartDate)
ModelState.AddModelError("StartDate", "Current value: "
+ String.Format("{0:d}", databaseValues.StartDate));
if (databaseValues.InstructorID != clientValues.InstructorID)
ModelState.AddModelError("InstructorID", "Current value: "
+ db.Instructors.Find(databaseValues.InstructorID).FullName);
ModelState.AddModelError(string.Empty, "The record you attempted to edit "
+ "was modified by another user after you got the original value. The "
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again. Otherwise click the Back to List hyperlink.");
departmentToUpdate.RowVersion = databaseValues.RowVersion;
}
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
ViewBag.InstructorID = new SelectList(db.Instructors, "ID", "FullName", departmentToUpdate.InstructorID);
return View(departmentToUpdate);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.