我想为问题跟踪应用做一个简单的编辑表单。为简单起见,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
我的编辑对象到上下文?
提前致谢
答案 0 :(得分:6)
你在做什么是棘手的,但可以使其发挥作用。我们假设您的时间戳字段被称为ConcurrencyToken
。显然,您必须在视图中包含此值并将其与表单一起提交。但是你不能简单地将它分配给POST中thisIssue.ConcurrencyToken
的值,因为EF会记住“旧”值(你通过调用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
来优化此功能,但这应该可以让您开始。
答案 1 :(得分:5)
使用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;
}