Compartilhar via


Handling Optimistic Concurrency Exception with EF and MVC 3

I've written a simple MVC 3 application to demonstrate handing optimistic concurrency exceptions with Entity Framework (EF). Browsers make it easy to test optimistic concurrency; right click on an edit link and select Open in New Tab, then select the same edit link on the page so you have the same record open for edit in two pages. Change a couple values in one window, hit the Save button, then go to the other tab. In the other Edit window, change a field and hit the Save button. The image below shows an example after the second Save.

The model validation errors show the current values (from our previous save). A previously hidden check box Force Save is displayed. You can use the Home link to cancel out of your changes, or you can check the Force Save box and all your changes will be saved.

A best practice is  to use a rowversion  to detect concurrency exceptions. The Product table of the AdventureWorksLT database doesn't include a rowversion field, so I added one with the name seqNum. To enable concurrency checking in the data model, open the EF model file (in my sample ProductsModel.edmx), right click the seqNum property of the Products entity and then click Properties. In the Properties window, change the ConcurrencyMode property to Fixed.

The following code will detect and handle concurrency conflicts:

 [HttpPost]
public ActionResult Edit(Product model) {

    try {

        if (!ModelState.IsValid) { return View(model); }

        db.Products.Attach(model);
        db.ObjectStateManager.ChangeObjectState(model, EntityState.Modified);
        db.SaveChanges();

        return RedirectToAction("Details", new { id = model.ProductID });

    } catch (OptimisticConcurrencyException ocex) {

        // Detach model to get current Product from DB
        db.Products.Detach(model);
        var cp = GetProduct(model.ProductID);
        AddModelErrors(cp, model);
        model.seqNum = cp.seqNum;

        ModelState.AddModelError("", "Optimistic Concurrency Exception occurred. Save Again to override");

    } catch (Exception ex) {
           ModelState.AddModelError("", "See exception and inner exception");  
    }

    return View(model);
}

When an optimistic concurrency exception is caught, the current data base values are fetched and displayed (when they differ with the current form values) as error messages. The current rowversion (sequence number) is also fetched so a subsequent save will persist to the DB in the absence of another change. If another user edits the row and saves it before you submit again, another optimistic concurrency exception will be detected.

 

You can download the sample here.

Comments

  • Anonymous
    February 22, 2011
    What I do not like here is the "remember" thing. When I delete the edmx and re-make( making changes to the database) then I should remember the setting. Could not be made somehow by default?

  • Anonymous
    February 24, 2011
    Good point. I think the EF team knows it would be benificial to save custom settings and apply them when you're forced to rebuild the model due to schema changes.

  • Anonymous
    April 12, 2011
    The comment has been removed

  • Anonymous
    March 15, 2012
    but what are the diferences between OptimisticConcurrencyException & DbUpdateConcurrencyException??