By: Joydip Kanjilal | Updated: 2019-07-29 | Comments | Related: > Entity Framework
Problem
Concurrency handling refers to the technique of detecting and resolving concurrency conflicts. It ensures safety, integrity and consistency of your data. Concurrency conflicts happen when two or more users try to access the same resource concurrently, i.e., at the same point of time. Entity Framework is an Object Relational Mapper (ORM) from Microsoft and Entity Framework Core is the version of Entity Framework that runs on .NET Core. How do we handle concurrency violations in Entity Framework Core? Check out this tip to find a solution.
Solution
You can detect concurrency violations in EF Core in two different ways. These include configuring the properties of the entities as concurrency tokens or by adding a "row version" property in the entity classes. We'll examine both one by one.
Pessimistic and optimistic concurrency
Concurrency conflicts can be of two types: pessimistic concurrency and optimistic concurrency. The Pessimistic concurrency technique applies an explicit lock on the shared resource, i.e., an explicit lock is performed on the record that is edited concurrently. On the contrary, in Optimistic concurrency handling technique, the last saved record wins - there is no lock on the shared resource. Entity Framework Core provides built-in support for optimistic concurrency. So, EF Core enables multiple processes or users to make changes to the same piece of data independently without the overhead of synchronization.
Detecting conflicts using concurrency tokens
To enable optimistic concurrency in Entity Framework Core, you can take advantage of the ConcurrencyCheck attribute. Assume that an update or delete operation is performed on an entity that has the ConcurrencyCheck attribute set on one or more of the properties of the entity. The EF Core runtime compares the value of the concurrency token, i.e., the value of the property of the entity that has the ConcurrencyCheck attribute set against the original value of the property that was read by EF Core. While comparing these values if there is a match the operation is performed successfully. If these values don't match, i.e., there is a concurrency conflict due to concurrent access to the same entity, the EF Core runtime reports the concurrency violation due to a conflicting operation on the entity by another user and the transaction is aborted.
The following code snippet shows how the ConcurrencyCheck attribute can be applied on a property of an entity class.
public class Employee { public int Id { get; set; } [ConcurrencyCheck] public string Code { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } }
Alternatively, you can configure the properties using the IsConcurrencyToken method as shown below.
public class PayrollDataContext : DbContext { public DbSet<Employee> Employees { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Employee>() .Property(a => a.Code).IsConcurrencyToken(); } }
Detecting conflicts by adding a RowVersion property
The other technique for detecting concurrency is using a row version on the entity. In this technique a column is added to the database table to store the version stamp of the record. Accordingly, a row version property is included in the model class as well. This version stamp column in the database table is increased each time data is inserted or modified. Let's now understand how concurrency conflicts occur. Assume that there are two users, namely UserA and UserB. Now suppose UserA and UserB have read a row of data (a record to be more precise) each from the database table. The row version value for both these users will be the same. Now suppose UserB has submitted his changes to the database. In doing so, the row version value will be increased. When UserA subsequently tries to modify the same record, the row version value read earlier will not match with the row version value in the database. Subsequently when you try to submit the changes to the database, the EF Core runtime will throw a DbUpdateConcurrencyException.
It should be noted that a property should be of type byte array to be mapped to a row version column. If you would want concurrency checks in multiple entities in your application, you can use a base entity class instead. The following code snippet illustrates how a base entity class can be created with two properties – Id and RowVersion having the data types as int and Timestamp respectively.
public class EntityBase { public int Id { get; set; } [Timestamp] public byte[] RowVersion { get; set; } }
Once the base entity class has been created; you can have the other entities in your application extend this class as shown in the code snippet given below.
public class Employee : EntityBase { public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } }
Resolving data concurrency conflicts
There are three types of values that are tracked by EF Core – these will help you resolve concurrency conflicts. These values include the following:
- Current values - this represents the current values that the application would write to the database
- Original values - this represents the values that were initially retrieved from the database
- Database values - this represents the values that are stored in the database
Refer to the code snippet below that shows how concurrency conflicts in EF Core can be resolved.
using (var dbContext = new PayrollDataContext()) { Employee employee = dbContext.Employees.Find(1); employee.Address = "Hyderabad, Telengana, INDIA"; try { dbContext.SaveChanges(); } catch (DbUpdateConcurrencyException ex) { ex.Entries.Single().Reload(); dbContext.SaveChanges(); } }
The SaveChanges method is called inside a try-catch block so that runtime exceptions can be handled. If a concurrency conflict occurs (due to concurrent updates to the same record) at the time when the SaveChanges method is called on the DbContext instance, an exception of type DbUpdateConcurrencyException will be thrown. Note that the Entries property of the DbUpdateConcurrencyException instance would provide you a list of the DbEntityEntry instances that correspond to the entities that could not be updated during a call to the SaveChanges method.
The Reload method in the catch block updates the current values of the entity in memory with those in the database. Once the updated data has been read into the entity, the SaveChanges method is called on the data context again to persist the changes to the database.
Next Steps
- Although EF Core provides support for optimistic concurrency control, it's up to you to decide the type of concurrency control mechanism to be implemented for your application.
- The choice between pessimistic or optimistic concurrency control is an important decision you need to take - there should be a balance between performance, scalability and data availability.
About the author
This author pledges the content of this article is based on professional experience and not AI generated.
View all my tips
Article Last Updated: 2019-07-29