Entity Framework  

How to Handle Concurrency in Entity Framework Core?

Concurrency handling is a critical aspect of building scalable, reliable applications with Entity Framework Core. In multi-user environments such as ASP.NET Core web applications, multiple users may attempt to update the same data simultaneously. Without proper concurrency control, this can result in data loss, inconsistent records, or unexpected overwrites. Entity Framework Core provides built-in mechanisms to manage concurrency effectively and maintain data integrity.

This article explains optimistic concurrency in EF Core, how concurrency tokens work, how to implement row versioning, and best practices for handling concurrency conflicts in production systems.

Understanding Concurrency in EF Core

Concurrency occurs when two or more users access and modify the same data simultaneously. There are two main types of concurrency control in database systems:

  • Optimistic Concurrency

  • Pessimistic Concurrency

Entity Framework Core primarily supports optimistic concurrency. In this model, EF Core assumes that conflicts are rare and checks for changes only when saving data.

If another user modifies the record after it is loaded, EF Core throws a DbUpdateConcurrencyException during SaveChanges().

What Is Optimistic Concurrency?

Optimistic concurrency works by comparing the original values with the current database values at the time of the update. If the values differ, a concurrency conflict is detected.

EF Core implements this using a concurrency token. A concurrency token is a property that tracks changes to a row.

Implementing Concurrency Using RowVersion

The most common way to handle concurrency in SQL Server with EF Core is by using a RowVersion column.

Step 1: Add a RowVersion Property

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }

    [Timestamp]
    public byte[] RowVersion { get; set; }
}

The [Timestamp] attribute marks RowVersion as a concurrency token.

Step 2: Configure in Fluent API (Optional)

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>()
        .Property(p => p.RowVersion)
        .IsRowVersion();
}

Step 3: Update Database

When using migrations, EF Core creates a rowversion column in SQL Server.

During update, EF Core generates SQL similar to:

UPDATE Products
SET Name = @Name, Price = @Price
WHERE Id = @Id AND RowVersion = @OriginalRowVersion;

If no rows are affected, EF Core throws DbUpdateConcurrencyException.

Handling DbUpdateConcurrencyException

You must handle concurrency exceptions gracefully in application code.

try
{
    await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
    foreach (var entry in ex.Entries)
    {
        if (entry.Entity is Product)
        {
            var proposedValues = entry.CurrentValues;
            var databaseValues = await entry.GetDatabaseValuesAsync();

            if (databaseValues == null)
            {
                Console.WriteLine("Entity was deleted by another user.");
            }
            else
            {
                entry.OriginalValues.SetValues(databaseValues);
            }
        }
    }
}

You can choose different conflict resolution strategies:

  • Client Wins (overwrite database values)

  • Database Wins (reload database values)

  • Merge Changes (manual resolution)

Using Concurrency Tokens Without RowVersion

Instead of RowVersion, you can mark any property as a concurrency token.

modelBuilder.Entity<Product>()
    .Property(p => p.Price)
    .IsConcurrencyToken();

In this case, EF Core compares the original Price value during update.

However, using RowVersion is recommended for SQL Server because it is automatically managed by the database engine.

Concurrency in ASP.NET Core Web Applications

In web applications, concurrency issues commonly occur when:

  • Two users edit the same form simultaneously

  • Admin dashboards modify shared records

  • Background jobs update the same data

To handle this properly:

  • Include RowVersion in your ViewModel

  • Send it back in hidden fields

  • Validate concurrency during update

Example ViewModel:

public class ProductViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public byte[] RowVersion { get; set; }
}

This ensures EF Core can detect if the record has changed between read and update.

Best Practices for Handling Concurrency in EF Core

  • Prefer optimistic concurrency for web applications

  • Use RowVersion with SQL Server

  • Always handle DbUpdateConcurrencyException

  • Provide user-friendly conflict messages

  • Log concurrency conflicts for monitoring

  • Avoid long-running transactions

Proper concurrency handling ensures database consistency while maintaining application scalability.

Summary

Handling concurrency in Entity Framework Core is essential for maintaining data integrity in multi-user applications. EF Core uses optimistic concurrency by default and detects conflicts through concurrency tokens such as RowVersion. When a conflict occurs, DbUpdateConcurrencyException is thrown, allowing developers to resolve it using strategies like client wins, database wins, or manual merging. By implementing RowVersion correctly, handling exceptions gracefully, and following best practices in ASP.NET Core applications, developers can build reliable and high-performance systems that prevent accidental data overwrites and ensure consistent database operations.