Concurrency control in Entity Framework Core

Concurrency control in Entity Framework Core

Concurrency control ensures data integrity when multiple users or processes interact with the database simultaneously.

The concurrency problem occurs when multiple users or processes attempt to read, write, or update the same data in a database simultaneously. Without concurrency control the data could be inconsistent, incorrect, or lost and it can lead to the famours problems as Lost updates, Dirty reads, Non-repeatable reads, Phantom reads and others.

EF Core supports concurrency control to ensure data integrity when multiple users or processes interact with the database simultaneously with several ways.


EF Core transactions

EF Core transactions ensure that a sequence of operations is treated as a single atomic unit. If any operation in the transaction fails, all operations are rolled back to maintain data consistency.

There is three different approaches:

1. Implicit Transactions

EF Core implicitly creates a transaction for database commands when SaveChanges() is called. If SaveChanges() fails, the changes are rolled back.

using (var context = new ApplicationDbContext())
{
    context.Add(new Product { Name = "Product A", Price = 100 });
    context.SaveChanges(); // Implicit transaction
}        

2. Explicit Transactions

Explicit transactions provide more control, allowing multiple SaveChanges() calls within the same transaction.

using (var transaction = context.Database.BeginTransaction())
 {
        try
        {
            context.Add(new Product { Name = "Product B", Price = 150 });
            context.SaveChanges();

            context.Add(new Product { Name = "Product C", Price = 200 });
            context.SaveChanges();

            transaction.Commit();
        }
        catch
        {
            transaction.Rollback();
            throw;
        }
 }
        

3. TransactionScope

Provides a higher-level API to manage transactions across multiple contexts or resources.

using (var scope = new TransactionScope())
{
    using (var context = new ApplicationDbContext())
    {
        context.Add(new Product { Name = "Product D", Price = 250 });
        context.SaveChanges();
    }

    using (var anotherContext = new AnotherDbContext())
    {
        anotherContext.Add(new Order { OrderNumber = "ORD123" });
        anotherContext.SaveChanges();
    }

    scope.Complete(); // Commit both operations
}        


Concurrency token

Concurrency token is a field used to detect conflicting updates when multiple users attempt to modify the same record. EF Core ensures that the original value of the token matches the current value in the database when SaveChanges() is called. If they don't match, a DbUpdateConcurrencyException will be thrown. This token could setup by [ConcurrencyCheck] attribute or Fluent API.

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

By marking a property as a concurrency token, EF Core ensures that the property is included in the WHERE clause of the UPDATE or DELETE SQL commands. This allows EF Core to compare the original value of the property (at the time of data retrieval) with its current value in the database. If the value of the concurrency token in the database has changed since it was last read, EF Core detects a conflict and throw a DbUpdateConcurrencyException is thrown when SaveChanges() is called.


RowVersion

EF Core supports versioning using a byte[] field. RowVersion property is the most automatic way to detect concurrency conflicts in EF Core. This designed to handle conflicts for the entire row, not just a specific property. When a RowVersion property introduced, EF Core automatically manages conflict detection and resolution at the database level using this property.

public byte[] RowVersion { get; set; }
modelBuilder.Entity<Product>()
    .Property(p => p.RowVersion)
    .IsRowVersion();        

The database generates and updates the value for each modification, and EF Core compares it to detect concurrency conflicts. Every time a row is updated, the database automatically generates a new RowVersion value. When EF Core sends an UPDATE or DELETE command, it includes the RowVersion value in the WHERE clause to ensure the row has not been modified by another transaction.


When to use this approaches

Transactions, Concurrency Tokens, and RowVersion each have different characteristics regarding reliability and performance, and their suitability depends on the application's requirements.

Transactions

Reliability - transactions provide the highest reliability for data integrity across multiple operations.

Performance - trnsactions introduce performance overhead, especially if they are long-running, as they may lock resources in the database, blocking other operations.

When to use - when a series of operations must succeed or fail together, or for critical operations where partial updates could corrupt data.

Concurrency tokens

Reliability - provide medium reliability for detecting conflicts at the property level.

Performance - concurrency tokens are lightweight since only the specified properties are checked for conflicts.

When to use - when you only need to track changes to specific properties.

RowVersion

Reliability - provides high reliability for detecting conflicts at the row level.

Performance - efficient. RowVersion is automatically managed by the database and optimized for performance

When to use: when you need to track changes to the entire row.

要查看或添加评论,请登录

Max Bazhenov的更多文章

  • Use try/catch with caution

    Use try/catch with caution

    The try/catch mechanism is used for handling exceptions, allowing the program to catch errors and handle them without…

  • Thread synchronization tools in details

    Thread synchronization tools in details

    Thread management in .NET offers multiple synchronization tools, each suited to different concurrency needs.

  • Parallelism management

    Parallelism management

    Parallelism in programming allows tasks to run concurrently, making full use of multi-core CPU architectures. By…

  • What is parallelism and how does it work

    What is parallelism and how does it work

    Previous posts covered asynchronous execution where we pass tasks to external resources and use the waiting time to do…

  • Best practices and antipatterns for Async/Await

    Best practices and antipatterns for Async/Await

    In the previous posts, we explored how async/await works, when to use them, and what benefits they provide. In this…

  • Await, Async, Task in depth

    Await, Async, Task in depth

    In the previous post, we explored how asynchronous methods free up threads in the thread pool and improve WebApp…

  • WebApp and system resources

    WebApp and system resources

    In the previous post, we introduced async operations and how they can optimize WebApp performance. But how exactly do…

    1 条评论
  • ToList() vs ToListAsync(). How does it work and where is benefit

    ToList() vs ToListAsync(). How does it work and where is benefit

    In this series of posts, we will look in depth at how async works and how the load is distributed between the WebApp…

  • Bundling and minificaiton

    Bundling and minificaiton

    Bundles and minificaiton in ASP.NET 4.

  • Angular standalone components

    Angular standalone components

    A standalone component is a type of component that doesn’t belong to any specific Angular module. Standalone component…

社区洞察

其他会员也浏览了