简体   繁体   English

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

[英]Optimistic concurrency model in Entity Framework and MVC

I have the following update code in the ASP.NET MVC controller: 我在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);
}

However, this update code is Last-Writer-Wins. 但是,此更新代码是Last-Writer-Wins。 Now I want to add some concurrency control. 现在我想添加一些并发控制。 The Person table already has the SQL timestamp column. Person表已经有SQL时间戳列。 Do I have to send the timestamp value to the client as the hidden value and process it manually in the post back? 我是否必须将时间戳值作为隐藏值发送给客户端并在回发中手动处理? Or is there aa standard pattern in Entity Framework to do this? 或者,实体框架中是否有标准模式来执行此操作?

Thanks. 谢谢。

First you need to define which property or properties will be used to perform the concurrency check, because concurrency is defined on a property-by-property basis in the Entity Framework. 首先,您需要定义将使用哪个或哪些属性来执行并发检查,因为并发性是在Entity Framework中逐个属性定义的。 ConcurrencyMode is used to flag a property for concurrency checking and can be found in the Entity Object Properties window (just right click on Person entity in your model). ConcurrencyMode用于标记用于并发检查的属性,可以在Entity Object Properties窗口中找到(只需右键单击模型中的Person实体)。 Its options are None , which is the default, and Fixed . 它的选项是None (默认值)和Fixed(固定)

During a call to SaveChanges , if a field has been changed in the DB since the row was retrieved, EF will cancel the Save and throw an OptimisticConcurrencyException if we set that field's ConcurrencyMode to Fixed . 在调用SaveChanges期间,如果在检索到行后数据库中的某个字段已更改,则EF将取消保存并抛出一个OptimisticConcurrencyException,如果我们将该字段的ConcurrencyMode设置为Fixed

Under the hood, EF includes that field's value in the Update or Delete SQL statement that is being Submitted to the data store as a WHERE clause. 在引擎盖下,EF在Update或Delete SQL语句中包含该字段的值,该语句作为WHERE子句提交到数据存储。

If you want to have Optimistic Concurrency on all properties, just set TimeStamp property ConcurrencyMode to Fixed you will get an OptimisticConcurrencyException if any field's value within the table get changed (instead of setting it to Fixed on every single property). 如果要在所有属性上使用Optimistic Concurrency,只需将TimeStamp属性ConcurrencyMode设置为Fixed ,如果表中的任何字段值发生更改(而不是在每个属性上将其设置为Fixed) ,则会得到OptimisticConcurrencyException。

EDIT 编辑
As per Craig comment below, you need to persist the TimeStamp in the view and read it back into Person object and the rest will be taken care of by EF if you set the ConcurrencyMode to fixed on the TimeStamp property. 根据下面的Craig评论,您需要在视图中保留TimeStamp并将其读回Person对象,如果您将ConcurrencyMode设置为TimeStamp属性上的固定,则其余部分将由EF处理。 You can of course try to handle OptimisticConcurrencyException that could be thrown by EF and there are ways to recover from this exception, if you are interested. 您当然可以尝试处理可能由EF引发的OptimisticConcurrencyException,如果您感兴趣,还有办法从此异常中恢复。

From MSDN Saving Changes and Managing Concurrency 从MSDN 保存更改和管理并发

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");
}

This is actually a little harder than it perhaps should be. 这实际上比它应该更难。 In addition to changing the concurrency mode to fixed, as Morteza says, you have to inject the concurrency value which you read during the GET before updating the entity during the POST. 除了将并发模式更改为固定之外,正如Morteza所说,在POST期间更新实体之前 ,必须注入在GET期间读取的并发值。 The way to think about this is that you're trying to get the entity back into the state it was in during the GET, before updating it. 考虑这一点的方法是,在更新之前,您正试图让实体回到GET期间的状态。 I have a code example in this answer: 我在这个答案中有一个代码示例:

ASP.NET MVC Concurrency with RowVersion in Edit Action 编辑操作中使用RowVersion的ASP.NET MVC并发

I ended up doing this in the postback function: 我最终在回发函数中执行此操作:

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();

So the optimistic concurrency is checked twice. 因此,检查乐观并发性两次。 Just wondering if there is a better way to do this? 只是想知道是否有更好的方法来做到这一点?

Thanks. 谢谢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM