I have the following update code in the 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 some concurrency control. The Person table already has the SQL timestamp column. Do I have to send the timestamp value to the client as the hidden value and process it manually in the post back? Or is there a a standard pattern in Entity Framework to do this?
Thanks.
EF Core implements optimistic concurrency control, meaning that it will let multiple processes or users make changes independently without the overhead of synchronization or locking. In the ideal situation, these changes will not interfere with each other and therefore will be able to succeed.
Test concurrency handlingChange a field in the first browser tab and click Save. The browser shows the Index page with the changed value. Click Save again. The value you entered in the second browser tab is saved along with the original value of the data you changed in the first browser.
Concurrency conflicts occur when one user retrieves an entity's data in order to modify it, and then another user updates the same entity's data before the first user's changes are written to the database.
EF provides two approaches for concurrency tokens: Applying [ConcurrencyCheck] or IsConcurrencyToken to a property on the model. This approach is not recommended. For more information, see Concurrency Tokens in EF Core.
First you need to define which property or properties will be used to perform the concurrency check, because concurrency is defined on a property-by-property basis in the Entity Framework. ConcurrencyMode is used to flag a property for concurrency checking and can be found in the Entity Object Properties window (just right click on Person entity in your model). Its options are None, which is the default, and Fixed.
During a call to SaveChanges, if a field has been changed in the DB since the row was retrieved, EF will cancel the Save and throw an OptimisticConcurrencyException if we set that field's ConcurrencyMode to Fixed.
Under the hood, EF includes that field's value in the Update or Delete SQL statement that is being Submitted to the data store as a WHERE clause.
If you want to have Optimistic Concurrency on all properties, just set TimeStamp property ConcurrencyMode to Fixed you will get an OptimisticConcurrencyException if any field's value within the table get changed (instead of setting it to Fixed on every single property).
EDIT
As per Craig comment below, you need to persist the TimeStamp in the view and read it back into Person object and the rest will be taken care of by EF if you set the ConcurrencyMode to fixed on the TimeStamp property. You can of course try to handle OptimisticConcurrencyException that could be thrown by EF and there are ways to recover from this exception, if you are interested.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With