我想为我们的问题跟踪应用程序做一个简单的编辑表单.为简单起见,HttpGet Edit操作看起来像这样:
// Issues/Edit/12 public ActionResult Edit(int id) { var thisIssue = edmx.Issues.First(i => i.IssueID == id); return View(thisIssue); }
然后HttpPost动作看起来像这样:
[HttpPost] public ActionResult Edit(int id, FormCollection form) { // this is the dumb part where I grab the object before I update it. // concurrency is sidestepped here. var thisIssue = edmx.Issues.Single(c => c.IssueID == id); TryUpdateModel(thisIssue); if (ModelState.IsValid) { edmx.SaveChanges(); TempData["message"] = string.Format("Issue #{0} successfully modified.", id); return RedirectToAction("Index"); } return View(thisIssue); }
这非常有效.但是,并发检查不起作用,因为在Post中,我正在尝试更新它之前重新检索当前实体.但是,对于EF,我不知道如何使用它的好看SaveChanges()
但是将我附加thisIssue
到上下文中.我试着打电话edmx.Issues.Attach(thisIssue)
但是我明白了
The object cannot be attached because it is already in the object context. An object can only be reattached when it is in an unchanged state.
如何使用EF处理MVC中的并发和/或如何将Attach
我编辑的对象正确地处理到上下文?
提前致谢
你在做什么是棘手的,但可以使其工作.让我们假设您的时间戳字段被调用ConcurrencyToken
.显然,您必须在视图中包含此值并将其与表单一起提交.但是您不能简单地将其分配给thisIssue.ConcurrencyToken
POST中的值,因为EF将记住"旧"值(您通过调用从DB获取的值Single()
以及"新"值(来自您的表单)并使用WHERE
子句中的"旧"值.所以你需要骗取EF并分配正确的值.试试这个:
var thisIssue = edmx.Issues.Single(c => c.IssueID == id); TryUpdateModel(thisIssue); // assign ConcurrencyToken var ose = Context.ObjectStateManager.GetObjectStateEntry(entityToUpdate); ose.AcceptChanges(); // pretend object is unchanged TryUpdateModel(thisIssue); // assign rest of properties
你可以通过仅绑定ConcurrencyToken
而不是调用TryUpdateModel
两次来优化它,但这应该让你开始.
下面是使用EF5执行乐观并发兼容更新的示例(该方法来自存储库.)假设实体定义时间戳[ConcurrencyCheck]
.调用时会发生并发异常DbContext.SaveChanges()
.
public TEntity Update(TEntity entity) { var attached = this.GetById(entity.Id); if (attached == null) { throw new MvcBootstrapDataException("{0} with Id = {1} does not exist.".F(typeof(TEntity).Description(), entity.Id)); } var entry = this.Context.Entry(attached); // The Timestamp must be in the original values for optimistic concurrency checking to occur. // Otherwise the context knows that the Timestamp has been modified in the context entry.OriginalValues["Timestamp"] = entity.Timestamp; entry.CurrentValues.SetValues(entity); attached.Modified = DateTime.Now; return attached; }