Optimistic concurrency model in Entity Framework and MVC

I have the following update code in an ASP.NET MVC controller:

[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); } 

However, this update code is Last-Writer-Wins. Now I want to add a concurrency control. The Person table already has an SQL timestamp column. Should I send the timestamp value to the client as a hidden value and process it manually in the message? Or is there a standard template in the Entity Framework for this?

Thanks.

+6
c # concurrency asp.net-mvc entity-framework
source share
4 answers

It is actually a little more complicated than it can be. In addition to changing the concurrency mode to fixed, as Morteza says, you must enter the concurrency value that you read during GET before updating the object during POST. The way to think about this is that you are trying to return the object back to the state it was in during the GET before updating it. There is an example code in this answer:

ASP.NET MVC concurrency with RowVersion in action Change

+2
source share

First you need to determine which property or properties will be used to perform the concurrency check, because concurrency is defined in properties by the properties in the Entity Framework. ConcurrencyMode is used to mark the properties for checking concurrency and can be found in the properties window of the Entity object (just right-click the Person object in your model). Its options are None , which is the default, and Fixed .

During a SaveChanges call, if the field has been changed in the database since the row was restored, EF will cancel the save and throw an OptimisticConcurrencyException if we set this ConcurrencyMode field to Fixed.

Under the hood, EF includes this field value in the SQL update or delete statement, which is passed to the data store as a WHERE clause.

If you want to have Optimistic concurrency for all properties, just set the TimeStamp ConcurrencyMode property to Fixed, you will get an OptimisticConcurrencyException if any field value in the table changes (instead of setting it to Fixed in each property).

EDIT
According to Craig's comment below, you need to save the TimeStamp in the view and read it back into the Person object, and EF will take care of the rest if you set the ConcurrencyMode parameter to the TimeStamp property. You can, of course, try to handle the OptimisticConcurrencyException that EF can throw, and there are ways to throw this exception if you are interested.

+3
source share

From MSDN Saving Changes and Concurrency Management

 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"); } 
+2
source share

I ended up doing this in the postback function:

 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 // ConcurrencyMode is set to fixed. ctx.SaveChanges(); 

Thus, optimistic concurrency is checked twice. Just wondering if there is a better way to do this?

Thanks.

0
source share

All Articles