实体框架和MVC中的乐观并发模型

时间:2010-10-10 01:35:08

标签: c# asp.net-mvc entity-framework concurrency

我在ASP.NET MVC控制器中有以下更新代码:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Person(int id, FormCollection form)
{
  var ctx = new DB_Entities(); // ObjectContext
  var person = ctx.Persons.Where(s => s.Id == id).FirstOrDefault(); 
  TryUpdateModel(person, form.ToValueProvider()); 
  ctx.SaveChanges(); 
  return RedirectToAction("Person", id);
}

但是,此更新代码是Last-Writer-Wins。现在我想添加一些并发控制。 Person表已经有SQL时间戳列。我是否必须将时间戳值作为隐藏值发送给客户端并在回发中手动处理?或者实体框架中是否有标准模式来执行此操作?

感谢。

4 个答案:

答案 0 :(得分:3)

首先,您需要定义将使用哪个或哪些属性来执行并发检查,因为并发是在Entity Framework中基于 property-by-property 的基础上定义的。 ConcurrencyMode 用于标记用于并发检查的属性,可以在“实体对象属性”窗口中找到(只需右键单击模型中的 Person 实体)。其选项为 ,这是默认设置, 已修复

在调用 SaveChanges 期间,如果在检索到行后数据库中的某个字段已更改,则EF将取消保存并抛出 OptimisticConcurrencyException 将该字段的 ConcurrencyMode 设置为 Fixed

在幕后,EF在Update或Delete SQL语句中包含该字段的值,该语句将作为WHERE子句提交到数据存储。

如果要在所有属性上使用Optimistic Concurrency,只需将TimeStamp属性ConcurrencyMode设置为 Fixed ,如果表中的任何字段值发生更改,您将获得OptimisticConcurrencyException(而不是在每个属性上将其设置为Fixed) )。

修改
根据下面的Craig评论,您需要在视图中保留 TimeStamp 并将其读回 Person 对象,如果您设置了其余部分,则由EF处理其余部分。 ConcurrencyMode修复了TimeStamp属性。您当然可以尝试处理可能由EF引发的OptimisticConcurrencyException,如果您有兴趣,还有办法从此异常中恢复。

答案 1 :(得分:2)

这实际上比它应该更难。除了将并发模式更改为固定之外,正如Morteza所说,您必须在POST期间注入在更新实体之前在GET 期间读取的并发值。考虑这一点的方法是,在更新之前,您正试图让实体回到GET期间的状态。我在这个答案中有一个代码示例:

ASP.NET MVC Concurrency with RowVersion in Edit Action

答案 2 :(得分:2)

来自MSDN Saving Changes and Managing Concurrency

try
{
    // Try to save changes, which may cause a conflict.
    int num = context.SaveChanges();
    Console.WriteLine("No conflicts. " +
        num.ToString() + " updates saved.");
}
catch (OptimisticConcurrencyException)
{
    // Resolve the concurrency conflict by refreshing the 
    // object context before re-saving changes. 
    context.Refresh(RefreshMode.ClientWins, orders);

    // Save changes.
    context.SaveChanges();
    Console.WriteLine("OptimisticConcurrencyException "
    + "handled and changes saved");
}

答案 3 :(得分:0)

我最终在回发函数中执行此操作:

var person = ctx.Persons.Where(s => s.Id == id).FirstOrDefault();
string ts = form.Get("person_ts"); // get the persisted value from form
if (person.TimeStamp != ts)
{
   throw new Exception("Person has been updated by other user");
}
TryUpdateModel(person, form.ToValueProvider());     
// EF will check the timestamp again if the timestamp column's 
// ConcurrencyMode is set to fixed.
ctx.SaveChanges();

因此,检查乐观并发两次。只是想知道是否有更好的方法来做到这一点?

感谢。