Learn more about SQL Server tools

mssqltips logo
 

Tutorials          DBA          Dev          BI          Career          Categories          Webcasts          Scripts          Today's Tip          Join

Tutorials      DBA      Dev      BI      Categories      Webcasts

DBA    Dev    BI    Categories

 

How to handle concurrency in Entity Framework Core


By:   |   Last Updated: 2019-07-29   |   Comments   |   Related Tips: More > 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.


Last Updated: 2019-07-29


get scripts

next tip button



About the author
MSSQLTips author Joydip Kanjilal Joydip Kanjilal - Microsoft MVP (2007 to 2012), Author and Speaker with more than 20 years of experience. Authored more than 500 articles, 8 books and reviewed a dozen books.

View all my tips




Post a comment or let the author know this tip helped.

All comments are reviewed, so stay on subject or we may delete your comment. Note: your email address is not published. Required fields are marked with an asterisk (*).

*Name    *Email    Email me updates 


Signup for our newsletter
 I agree by submitting my data to receive communications, account updates and/or special offers about SQL Server from MSSQLTips and/or its Sponsors. I have read the privacy statement and understand I may unsubscribe at any time.



    



Learn more about SQL Server tools