How to handle concurrency conflicts in Entity Framework

Explore ways to handle concurrency conflicts to maintain data integrity and data consistency when working with Entity Framework

Concurrency Handling in EF

Concurrency Handling in EF

Concurrency handling can be used to maintain data integrity and data consistency when multiple users access the same resource concurrently. Concurrency violations can occur when you have interdependent transactions, i.e. transactions that are dependent on one another and try to access the same resource.

Handling concurrency conflicts in Entity Framework

Let's now understand how each of these strategies work in Entity Framework. In the pessimistic concurrency, when a particular record is being updated, all other concurrent updates on the same record will be put on hold until the current operation is complete and the control is relinquished back so that other concurrent operations can continue. In the optimistic concurrency mode, the last saved record, "wins." In this mode, it's assumed that resource conflicts due to concurrent accesses to a shared resource are unlikely, but, not impossible.

Incidentally, Entity Framework provides support for optimistic concurrency by default. Entity Framework doesn't provide support for pessimistic concurrency out of the box. Let's now understand how Entity Framework resolves concurrency conflicts when working in the optimistic concurrency (default mode).

When working with optimistic concurrency handling mode, you would typically want to save data to your database assuming that the data hasn't changed since it was loaded in the memory. Note that when your attempt to save changes to the database using the SaveChanges method on your data context instance, a DbUpdateConcurrencyException will be thrown. Let's now understand how we can fix this.

To check for concurrency violation you can include a field in your entity class and mark it using Timestamp attribute. Refer to the entity class given below.

public class Author

   {

       public Int32 Id { get; set; }

       public string FirstName { get; set; }

       public string LastName { get; set; }

       public string Address { get; set; }

       [Timestamp]

       public byte[] RowVersion { get; set; }

   }

Now, Entity Framework supports two concurrency modes: None and Fixed. While the former implies that no concurrency checks would be performed when updating the entity, the latter implies that the original value of the property will be considered while executing WHERE clauses at the time when updates or deletes of data are done. If you have a property that is marked using Timestamp, the concurrency mode is considered as Fixed which in turn implies that the original value of the property would be considered in the WHERE clause of any updates or deletes of data for that particular entity.

To resolve optimistic concurrency conflicts, you can take advantage of the Reload method to update the current values in your entity residing in the memory with the recent values in the database. Once reloaded with the updated data, you can attempt to persist your entity again in the database. The following code snippet illustrates how this can be achieved.

using (var dbContext = new IDBDataContext())

{

     Author author = dbContext.Authors.Find(12);

     author.Address = "Hyderabad, Telengana, INDIA";

       try

         {

             dbContext.SaveChanges();

         }

         catch (DbUpdateConcurrencyException ex)

         {

             ex.Entries.Single().Reload();

             dbContext.SaveChanges();

         }

}

Note that you can leverage the Entries method on the DbUpdateConcurrencyException instance to retrieve the list of DbEntityEntry instances corresponding to the entities that could not be updated when a SaveChanges method was called to persist the entities to the database.

Now, the approach we just discussed is often called "stored wins" or "database wins" since the data contained in the entity is overwritten by the data available in the database. You can also follow another approach called "client wins". In this strategy, the data from the database is retrieved to populate the entity. In essence, the data retrieved from the underlying database is set as the original values for the entity. The following code snippet illustrates how this can be achieved.

try

{

     dbContext.SaveChanges();

}

catch (DbUpdateConcurrencyException ex)

{

   var data = ex.Entries.Single();

   data.OriginalValues.SetValues(data.GetDatabaseValues());

}

You can also check if the entity you are trying to update is already deleted by another user or, has already been updated by another user. The following code snippet illustrates how you can do this.

catch (DbUpdateConcurrencyException ex)

{

   var entity = ex.Entries.Single().GetDatabaseValues();

   if (entity == null)

   {

         Console.WriteLine("The entity being updated is already deleted by another user...");

   }

   else

   {

         Console.WriteLine("The entity being updated has already been updated by another user...");

   }

}

If your database table doesn't have a timestamp column or rowversion, you can take advantage of the ConcurrencyCheck attribute to detect concurrency conflicts when using Entity Framework. Here's how this property is used.

[Table("Authors"]

public class Author

{

   public Author() {}

   [Key]

   public int Id { get; set; }

   [ConcurrencyCheck]

   public string FirstName { get; set; }

   public string LastName { get; set; }

   public string Address { get; set; }

}

In doing do, SQL Server would automatically include AuthorName when executing update or delete statements in the database.

This article is published as part of the IDG Contributor Network. Want to Join?

To comment on this article and other InfoWorld content, visit InfoWorld's LinkedIn page, Facebook page and Twitter stream.
From CIO: 8 Free Online Courses to Grow Your Tech Skills
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.