I’m wanting to do a simple edit form for our Issue Tracking app. For simplicity, the HttpGet Edit action looks something like this:
// Issues/Edit/12
public ActionResult Edit(int id)
{
var thisIssue = edmx.Issues.First(i => i.IssueID == id);
return View(thisIssue);
}
and then the HttpPost action looks something like this:
[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);
}
Which works wonderfully. However, the concurrency check doesn’t work because in the Post, I’m re-retreiving the current entity right before I attempt to update it. However, with EF, I don’t know how to use the fanciness of SaveChanges() but attach my thisIssue to the context. I tried to call edmx.Issues.Attach(thisIssue) but I get
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.
How do I handle concurrency in MVC with EF and/or how do I properly Attach my edited object to the context?
Thanks in advance
What you are doing is tricky, but can be made to work. Let’s presume your timestamp field is called
ConcurrencyToken. Obviously, you must include this value in the View and submit it with your form. But you can’t simply assign that to the value tothisIssue.ConcurrencyTokenin the POST because the EF will remember both the “old” value (the value you fetched from the DB with your call toSingle()as well as the “new” value (from your form) and use the “old” value in theWHEREclause. So you need to lie to the EF and assign the correct value. Try this:You can optimize this by binding only
ConcurrencyTokeninstead of callingTryUpdateModeltwice, but this should get you started.