Xceedas

Xceedas
xceedas

Monday, 22 September 2014

Handling Concurrency in MVC and EF

This article shows how to handle concurrency in MVC and EF. I am assuming that you have some idea of the Fluent API.
Concurrency Issue
 
It is an important issue in real-world development. The webs we develop are used by thousands of users. Concurrency occurs when two users want to modify/delete the same entity at the same time. In other words, suppose user B wants to update the entity before the user A committed the change he made to that same user. If you don't handle the situation only whoever commits last, that update will be reflected. In my applications this risk is non-critical and okay for the requirements perspective. In that case you need not to bother about this issue. But some projects really want to handle the concurrency issues.
 
Optimistic Concurrency
 
We can implement concurrency in two ways. Pessimistic Concurrency (Locking) and Optimistic Concurrency. Pessimistic Concurrency deals with database locking and is out of context for this article since it is more specific to Windows applications. We will see what Optimistic Concurrency is.
 
From Wikipedia the definition of Optimistic Concurrency is:
 
"Optimistic concurrency is a concurrency method that assumes that multiple transactions can complete without affecting each other and that therefore transactions can proceed without locking the data resources that they affect. Before committing, each transaction verifies that no other transaction has modified its data. If the check reveals conflicting modifications, the committing transaction rolls back."
 
Implementation
 
There can be various approaches available for concurrency handling. The procedure used most is known as a Store Wins scenario. In this approach we prevent user B's change being updated in the database. Typically user B will receive an error message showing him/her the current state of the data and allow him/her to reapply the change if he/she really wants to make the change.
 
Step 1
 
Include a tracking column that can be used to determine when the row is changed. The datatype of this tracking column is generally rowversion. If the user writes an Update or Delete command then the where clause will include the original version of that column. If the row being updated has been changed by another user, the value in the rowversion column is different from the original value, then the statement will not find the row to update/delete and is interpreted as a concurrency conflict.
 
Step 2

In your model class add a property: 
  1. [Timestamp]  
  2. public byte[] RowVersion { getset; }  
The Timestamp attribute specifies that this column will be included in the Where clause of Update and Delete commands.
 
Step 3
 
Set the Concurrency token in the model builder: 
  1. modelBuilder.Entity<YourEntityName>()  
  2. Property(p => p.RowVersion).IsConcurrencyToken();  
Update the database from the Package Manager Console (PMC).
 
Step 4
 
Now if the concurrency conflict occurs then the update/delete statement will generate a DbUpdateConcurrencyException. So we need to handle this exception and show the appropriate something.
  1. catch (DbUpdateConcurrencyException ex)  
  2. {  
  3.     var entity= ex.Entries.Single().GetDatabaseValues();  
  4.     if (entity == null)  
  5.     {  
  6.          ModelState.AddModelError(string.Empty,  
  7.             "Unable to save changes. The entity you are trying to update was deleted by another user.");  
  8.     }  
  9.     else  
  10.     {  
  11.          ModelState.AddModelError(string.Empty, “ The record you want to edit has been updated by another user. If you still want to make the change then click the “Save” button again. Otherwise click the back button to discard!”);  
  12.         MyEntity.RowVersion = (MyEntity)databaseEntry.ToObject().RowVersion;  
  13.     }  
  14. }  
Step 5
 
The view will store the original RowVersion value in a hidden field as in the following:
  1. @Html.HiddenFor(model => model.RowVersion)  

No comments :