EF Core Concurrency Discover How to Implement Optimistic Concurrency
Introduction
Database concurrency refers to situations in which multiple processes or users access or change the same data in a database at the same time.
- EF Core implements optimistic concurrency control, and it means that multiple processes or users can make changes independently and these changes will not interfere with each other.
- In the worst case scenario, two or more processes will attempt to make conflicting changes, and only one of them should succeed.
Concurrency Control
To implement optimistic concurrency control, you need to confugure properties as concurrency tokens. So whenever an update or delete operation is performed during SaveChanges, the value of the concurrency token on the database is compared against the original value read by EF Core.
- If the values match, the operation can complete.
- If the values do not match, EF Core assumes that another user has performed a conflicting operation and aborts the current transaction.
- Database providers are responsible for implementing the comparison of concurrency token values.
Concurrency Conflict
It is a situation when another user has performed an operation that conflicts with the current operation.
In Entity Framework Core, there are two approaches to detect concurrency conflict detection
- Configuring existing properties as concurrency tokens
- Adding a new "rowversion" property to act as a concurrency token.
Configure Existing Properties
The simplest way to configure existing properties is by using the data annotations [ConcurrencyCheck]
attribute.
public class Customer { public int CustomerId { get; set; } [ConcurrencyCheck] public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } }
The [ConcurrencyCheck]
attribute specify that a property should be included in a WHERE clause in an UPDATE or DELETE statement as part of concurrency management.
You can also use the Fluent API IsConcurrencyToken()
method to configure the existing properties.
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Customer>() .Property(c => c.FirstName).IsConcurrencyToken(); }
Add Additional RowVersion Property
A RowVersion
is a property where a new value is generated by the database every time a row is inserted or updated and it is also treated as a concurrency token.
- This ensures you will get an exception if anyone else has modified a row that you are trying to update since you queried for the data.
- The database provider is responsible to achieve this, for SQL Server, it is usually used on a byte[] property, which will be setup as a
ROWVERSION
column in the database.
You can use the data annotations [Timestamp]
attribute to specifies the data type of the column as a row version.
public class Customer { public int CustomerId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } [Timestamp] public byte[] RowVersion { get; set; } }
Another way of configuring a property as a timestamp/rowversion is to use the Fluent API IsRowVersion()
method.
public class Customer { public int CustomerId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } public byte[] RowVersion { get; set; } public virtual List<Invoice> Invoices { get; set; } } class MyContext : DbContext { public DbSet<Customer> Customers { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Customer>() .Property(c => c.RowVersion) .IsRowVersion(); } }
ZZZ Projects