Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/332.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 实体框架和MVC中的乐观并发模型_C#_Asp.net Mvc_Entity Framework_Concurrency - Fatal编程技术网

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

C# 实体框架和MVC中的乐观并发模型,c#,asp.net-mvc,entity-framework,concurrency,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

我在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);
}
但是,此更新代码是最后一个Writer获胜的代码。现在我想添加一些并发控制。Person表已具有SQL时间戳列。我是否必须将时间戳值作为隐藏值发送给客户端,并在回发中手动处理它?或者在实体框架中是否有一个标准模式来实现这一点


谢谢。

首先,您需要定义将用于执行并发性检查的一个或多个属性,因为并发性是在实体框架中逐个属性定义的用于标记并发检查的属性,可以在实体对象属性窗口中找到(只需右键单击模型中的Person实体)。其选项为默认的None,以及Fixed

在调用SaveChanges期间,如果在检索行之后数据库中的某个字段发生了更改,则如果我们将该字段的并发模式设置为Fixed,EF将取消保存并抛出一个错误

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

如果希望在所有属性上都具有乐观并发性,只需将TimeStamp属性ConcurrencyMode设置为Fixed。如果表中任何字段的值发生更改(而不是在每个属性上都将其设置为Fixed),则将获得OptimisticConcurrencyException。

编辑

根据下面的Craig评论,您需要在视图中持久化时间戳,并将其读回Person对象,如果您在TimeStamp属性上将ConcurrencyMode设置为fixed,其余的将由EF处理。当然,您可以尝试处理EF可能引发的OptimisticConcurrencyException,如果您感兴趣,还有一些方法可以从该异常中恢复。

我最后在回发函数中完成了这项操作:

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();
因此,乐观并发将被检查两次。只是想知道有没有更好的方法


谢谢。

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

来自MSDN


他确实需要在视图中包含并发令牌,因为他想知道自GET之后是否有人更新了实体。这只有在您知道GET期间的值时才可能。然而,在POST期间重新获取实体会使事情变得复杂,因为它会破坏EF内置的并发控制。EF将使用您在POST期间获取的值,而不是GET中的值。是的,您是对的,他显然需要从他的视图中读取时间戳。他在帖子里又读了一次《人物》,这让我很困惑。我更新了我的答案。克雷格,非常感谢你在这方面的投入。谢谢。我认为Craig的解决方案(使用GetObjectStateEntry.AcceptChanges假装原始时间戳)效果更好。顺便说一句,我认为根本原因是文章中再次读取了Person实体,因此它可能不是最初呈现页面时的同一实体。这是EF中正确的编程模型吗?还有其他方法可以避免这种情况吗?我认为您的代码甚至不会编译:Operator'!='无法应用于“byte[]”和“string”类型的操作数。无论如何,即使您将其编译,这仍然不是一个检查并发性的健壮的编程模型,因为有人可能会在您的if语句之后和SaveChanges()之前更改行,您将很不走运地发现这一点。很抱歉这么简单“!=”,不管怎么说,您都明白了(假设有一些辅助方法来进行比较)。这就是为什么我说在我的方法中并发性检查要做两次:一次在我的“if”语句中,另一次在EF SaveChanges()中使用ConcurrencyMode。如果有人在If语句之后和SaveChanges之前更改了行,EF将捕获它。Ian,在POST期间重新读取实体就可以了。这不是必需的,但这样做没有问题。
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");
}