Back to: ASP.NET Core Tutorials For Beginners and Professionals
ConcurrencyCheck Attribute in Entity Framework Core
In this article, I am going to discuss ConcurrencyCheck Data Annotation Attribute in Entity Framework Core (EF Core) with Examples. Please read our previous article, where we discussed TimeStamp Attribute in Entity Framework Core with Examples. The [ConcurrencyCheck] attribute is used in Entity Framework Core to mark a property or properties being used for optimistic concurrency control. This attribute helps Entity Framework Core determine whether a concurrent update has occurred on an entity while it’s being saved to the database.
ConcurrencyCheck Attribute in Entity Framework Core
The ConcurrencyCheck Data Annotation Attribute can be applied to one or more properties of an entity in Entity Framework Core. When we apply the ConcurrencyCheck Attribute to a property, then the corresponding column in the database table will be used in the optimistic concurrency check using the where clause.
So, using the ConcurrencyCheck Attribute in Entity Framework Core is another way to handle the Concurrency Issues. The Concurrency issue arises when multiple users or transactions attempt to update/delete the same row simultaneously. We can handle this Concurrency issue using the ConcurrencyCheck column.
If you go to the definition of ConcurrencyCheck Attribute in EF Core, then you will see the following. As you can see, this class has a parameterless constructor.
Example to Understand ConcurrencyCheck Attribute in EF Core:
Let us understand ConcurrencyCheck Data Annotation Attribute in Entity Framework Core with an example. Please modify the Student Entity class as follows. Here, you can see we have applied the ConcurrencyCheck Attribute on the Name and RegdNumber Properties. In EF Core, we can apply the ConcurrencyCheck Attribute with one or more properties of an Entity.
using System.ComponentModel.DataAnnotations; namespace EFCoreCodeFirstDemo.Entities { public class Student { public int StudentId { get; set; } [ConcurrencyCheck] public int RegdNumber { get; set; } [ConcurrencyCheck] public string? Name { get; set; } public string? Branch { get; set; } } }
Next, modify the context class as follows:
using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace EFCoreCodeFirstDemo.Entities { public class EFCoreDbContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { //To Display the Generated the Database Script optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information); //Configuring the Connection String optionsBuilder.UseSqlServer(@"Server=LAPTOP-6P5NK25R\SQLSERVER2022DEV;Database=EFCoreDB;Trusted_Connection=True;TrustServerCertificate=True;"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { } public DbSet<Student> Students { get; set; } } }
Next, modify the Main Method of the Program class as follows. Here, you can see we have added two entities to the database. Unlike TimeStamp, we need to provide values for [ConcurrencyCheck] attribute properties.
using EFCoreCodeFirstDemo.Entities; namespace EFCoreCodeFirstDemo { internal class Program { static void Main(string[] args) { try { using EFCoreDbContext context = new EFCoreDbContext(); var student1 = new Student() { Name = "Pranaya", Branch = "CSE", RegdNumber = 1001 }; context.Students.Add(student1); var student2 = new Student() { Name = "Hina", Branch = "CSE", RegdNumber = 1002 }; context.Students.Add(student2); context.SaveChanges(); Console.WriteLine("Students Added"); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); ; } } } }
Note: Before Proceeding further, let us delete the EFCoreDB database using SSMS and Migration folder from our project.
With the above changes in place, open Package Manager Console and Execute the following add-migration and update-database commands. You can give any name to your migration. Here, I am giving EFCoreDBMig1. The name that you are giving it should not be given earlier.
Now, run the application, and you will see the records are being inserted into the Students database table as expected, as shown in the below image.
The ConcurrencyCheck column(s) will be included in the where clause whenever we update or delete an entity and call the SaveChanges method. We have already added two students with StudentId 1 and 2. Let us update the Name and Branch of the Student whose Id is 1. To do so, modify the Main Method of the Program class as follows.
using EFCoreCodeFirstDemo.Entities; namespace EFCoreCodeFirstDemo { internal class Program { static void Main(string[] args) { try { using EFCoreDbContext context = new EFCoreDbContext(); //Fetch the Student Details whose Id is 1 var studentId1 = context.Students.Find(1); if (studentId1 != null) { studentId1.Name = "Name Updated"; studentId1.Branch = "Branch Updated"; context.SaveChanges(); } Console.ReadKey(); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); ; } } } }
When you run the above code, you will get the following output. Here, you can see, in the where clause along with the Primary Key Column, it is also using the Name and RegdNumber columns. This is to handle the concurrency issues.
Handling Concurrency Issues using ConcurrencyCheck in Entity Framework:
A concurrency token is a value used to check a database record for updates. If the value has changed, the update will fail. This can happen when two users edit the same record simultaneously.
Let us understand how to handle concurrency issues with Entity Framework using the ConcurrencyCheck column. Please modify the Main Method of the Program class as follows. Here, we are updating the same student using two different threads at the same time. Both Method1 and Method2 reads the student entity whose Id is 1 and also read the same Name and RegdNumber values.
Let us assume Method 1 starts updating first. So, he will update the data in the database and also update the Name and RegdNumber column values. Now, Method2 tries to update the same entity. If you remember, while updating, it will use the Name and RegdNumber columns in the where clause, but that Method1 has already modified Name and RegdNumber column value with the Method2 entity. So, Method2 has Name and RegdNumber values that do not exist anymore in the database, and hence Method2 SaveChanges method is going to throw an exception showing concurrency issues. It might be possible that thread2 starts its execution first, and in that case, Method1 is going to throw an exception.
using EFCoreCodeFirstDemo.Entities; namespace EFCoreCodeFirstDemo { internal class Program { static void Main(string[] args) { try { Console.WriteLine("Main Method Started"); Thread t1 = new Thread(Method1); Thread t2 = new Thread(Method2); t1.Start(); t2.Start(); t1.Join(); t2.Join(); Console.WriteLine("Main Method Completed"); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); ; } } public static void Method1() { using EFCoreDbContext context = new EFCoreDbContext(); //Fetch the Student Details whose Id is 1 var studentId1 = context.Students.Find(1); //Before Updating Delay the Thread by 2 Seconds Thread.Sleep(TimeSpan.FromSeconds(2)); if (studentId1 != null) { studentId1.Name = studentId1.Name + "Method1"; studentId1.Branch = studentId1.Branch + "Method1"; context.SaveChanges(); Console.WriteLine("Student Updated by Method1"); } } public static void Method2() { using EFCoreDbContext context = new EFCoreDbContext(); //Fetch the Student Details whose Id is 1 var studentId1 = context.Students.Find(1); //Before Updating Delay the Thread by 2 Seconds Thread.Sleep(TimeSpan.FromSeconds(2)); if (studentId1 != null) { studentId1.Name = studentId1.Name + " Method2"; studentId1.Branch = studentId1.Branch + " Method2"; context.SaveChanges(); Console.WriteLine("Student Updated by Method2"); } } } }
You will get the following exception when you run the above application, which makes sense.
Differences Between TimeStamp and ConcurrencyCheck Attribute in EF Core:
In Entity Framework Core, both the [Timestamp] and [ConcurrencyCheck] attributes are related to optimistic concurrency control, but they serve slightly different purposes.
[Timestamp] Attribute:
The [Timestamp] attribute is used to specify that a property in an entity class represents a timestamp or row version column in the database. This attribute is typically applied to a byte[] property. The database automatically updates this property’s value whenever a row is modified.
The [Timestamp] attribute is mainly used to detect conflicts between concurrent updates. When you save changes to an entity, Entity Framework Core checks if the current value of the timestamp property in the database matches the value that was originally retrieved when the entity was loaded. If the values don’t match, it indicates that another process has updated the same record in the database.
[ConcurrencyCheck] Attribute:
The [ConcurrencyCheck] attribute marks a property used for optimistic concurrency control, indicating that this property should be considered when checking for conflicts during updates. This attribute can be applied to any numeric, datetime, or byte array property. When an entity is saved, Entity Framework Core compares the current value of the [ConcurrencyCheck]-marked property or properties with the originally retrieved value. If the values don’t match, a concurrency conflict is detected.
Here are the key differences between the two attributes:
- [Timestamp] is specifically used to represent a timestamp or row version column, and the database manages the value.
- [ConcurrencyCheck] is used to mark any property for optimistic concurrency control, and your application logic manages the value.
- When using [Timestamp], the database automatically updates the value upon every modification, ensuring that it reflects the last modification time.
- [ConcurrencyCheck] requires you to manage the value yourself; it’s typically incremented manually in your application logic whenever an update is made.
- We can create only one property with a TimeStamp attribute that is too only on a byte[] type property. On the other hand, we can apply the ConcurrencyCheck attribute on multiple properties, which is too many different data types like string, int, etc.
In summary, both attributes help with optimistic concurrency control, but [Timestamp] is often used for tracking modifications’ timing, while [ConcurrencyCheck] is more flexible and can be applied to various properties used for concurrency control. Always consider your specific requirements when deciding which attribute to use.
In the next article, I am going to discuss Fluent API in Entity Framework Core with Examples. In this article, I try to explain ConcurrencyCheck Data Annotation Attribute in Entity Framework Core with Examples. I hope you enjoyed this ConcurrencyCheck Attribute in EF Core with Examples article.