Handling Concurrency in EF Core

What is Concurrency?

Concurrency handling prevents conflicts when multiple users try to update the same data simultaneously. EF Core uses optimistic concurrency by default.

Concurrency Tokens

Using Timestamp

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

// Fluent API
modelBuilder.Entity<Product>()
    .Property(p => p.RowVersion)
    .IsRowVersion();

Using ConcurrencyCheck

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

Handling Concurrency Conflicts

try
{
    var product = await context.Products.FindAsync(id);
    product.Price = newPrice;
    await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
    var entry = ex.Entries.Single();
    var databaseValues = await entry.GetDatabaseValuesAsync();
    
    if (databaseValues == null)
    {
        // Entity was deleted
        Console.WriteLine("Entity was deleted by another user");
    }
    else
    {
        // Entity was modified
        var databaseEntity = (Product)databaseValues.ToObject();
        Console.WriteLine($"Database value: {databaseEntity.Price}");
        
        // Resolve conflict
        entry.OriginalValues.SetValues(databaseValues);
        await context.SaveChangesAsync();
    }
}

Conflict Resolution Strategies

Client Wins

catch (DbUpdateConcurrencyException ex)
{
    var entry = ex.Entries.Single();
    var databaseValues = await entry.GetDatabaseValuesAsync();
    entry.OriginalValues.SetValues(databaseValues);
    await context.SaveChangesAsync();
}

Database Wins

catch (DbUpdateConcurrencyException ex)
{
    var entry = ex.Entries.Single();
    await entry.ReloadAsync();
}

Merge Values

catch (DbUpdateConcurrencyException ex)
{
    var entry = ex.Entries.Single();
    var proposedValues = entry.CurrentValues;
    var databaseValues = await entry.GetDatabaseValuesAsync();
    
    foreach (var property in proposedValues.Properties)
    {
        var proposedValue = proposedValues[property];
        var databaseValue = databaseValues[property];
        
        // Custom merge logic
        if (property.Name == "Price")
        {
            proposedValues[property] = Math.Max(
                (decimal)proposedValue, 
                (decimal)databaseValue);
        }
    }
    
    entry.OriginalValues.SetValues(databaseValues);
    await context.SaveChangesAsync();
}

Summary

EF Core uses optimistic concurrency with tokens like Timestamp or ConcurrencyCheck. Handle DbUpdateConcurrencyException to resolve conflicts using client wins, database wins, or merge strategies.

Test Your Knowledge

Take a quick quiz to test your understanding of this topic.

Test Your Efcore Knowledge

Ready to put your skills to the test? Take our interactive Efcore quiz and get instant feedback on your answers.