ConcurrencyCheck Attribute in Entity Framework Core

ConcurrencyCheck Attribute in Entity Framework Core

In this article, I will discuss ConcurrencyCheck Data Annotation Attribute in Entity Framework Core (EF Core) with Examples. Please read our previous article discussing 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.

In Entity Framework Core (EF Core), the ConcurrencyCheck attribute indicates that a particular property should be included in concurrency conflict detection. When a property is marked with this attribute, EF Core will include it in the WHERE clause of UPDATE and DELETE commands, ensuring that the value hasn’t changed since the entity was fetched.

If you go to the definition of ConcurrencyCheck Attribute in EF Core, you will see the following. As you can see, this class has a parameterless constructor.

ConcurrencyCheck Attribute in Entity Framework Core

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. We have applied the ConcurrencyCheck Attribute on the Name and RegdNumber Properties here. 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 must 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, open the Package Manager Console and Execute the add-migration and update-database commands as follows. 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.

ConcurrencyCheck Data Annotation Attribute in EF Core

Now, run the application, and you will see the records are being inserted into the Students database table as expected, as shown in the image below.

ConcurrencyCheck Data Annotation Attribute in Entity Framework Core

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 also uses the Name and RegdNumber columns. This is to handle the concurrency issues.

ConcurrencyCheck Data Annotation Attribute inEF Core with Examples

Handling Concurrency Issues using ConcurrencyCheck in Entity Framework:

A concurrency token is a value that checks 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.

Handling Concurrency Issues using ConcurrencyCheck in Entity Framework

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 simultaneously. Both Method1 and Method2 read 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 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 Method1 has already modified the Name and RegdNumber column value with the Method2 entity. So, Method2 has Name and RegdNumber values that no longer exist in the database, and hence Method2 SaveChanges method will throw an exception showing concurrency issues. It might be possible that thread2 starts its execution first; in that case, Method1 will 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.

ConcurrencyCheck Data Annotation Attribute in Entity Framework Core with Examples

Advantages and Disadvantages of ConcurrencyCheck Attribute in Entity Framework Core

The ConcurrencyCheck attribute in Entity Framework Core (EF Core) enables developers to define properties that should be considered in concurrency conflict detection. This offers more control over how EF Core detects and manages concurrent edits on data. Let’s delve into the advantages and disadvantages of using the ConcurrencyCheck attribute:

Advantages of ConcurrencyCheck Attribute in Entity Framework Core:
  • Granular Concurrency Control: Provides fine-grained control over which specific properties of an entity should be considered for concurrency checks. This is useful for targeting fields more likely to have concurrent updates.
  • Intuitive Mapping: For some application scenarios, checking specific fields for concurrency issues is more intuitive. For instance, detecting if a shared document’s content or title has changed in a collaborative application can be more relevant than a generic timestamp.
  • Flexibility: Multiple properties within an entity can be marked with the ConcurrencyCheck attribute, offering a broader range of checks for potential data conflicts.
  • Optimistic Concurrency: Helps in implementing optimistic concurrency control without needing a separate RowVersion or timestamp column.
Disadvantages of ConcurrencyCheck Attribute in Entity Framework Core:
  • Performance Implications: Adding the ConcurrencyCheck attribute to multiple properties can lead to more extensive WHERE clauses in UPDATE and DELETE SQL statements. This might affect performance, especially in large tables or when many properties are marked for concurrency checks.
  • Increased Conflict Potential: By checking specific property values, there’s an increased chance of detecting conflicts, even in scenarios where changes by different users might be logically compatible but still trigger a conflict.
  • Complex Conflict Resolution: Resolving detected conflicts can become more complicated based on specific properties rather than a general timestamp or version number.
  • Database Schema: Unlike a Timestamp field, which might be used exclusively for concurrency control, fields marked with ConcurrencyCheck often have domain significance. Changes to the semantics or usage of these fields can inadvertently impact concurrency behavior.
  • Maintenance Overhead: As the domain model evolves, developers must consistently review and possibly update which properties should have the ConcurrencyCheck attribute, ensuring the right fields are checked for concurrency conflicts.

While the ConcurrencyCheck attribute provides a nuanced approach to managing optimistic concurrency, it comes with challenges. It’s essential to weigh its benefits against its complexities, keeping in mind the application’s specific requirements and the frequency of concurrent edits on data. The choice of ConcurrencyCheck should be driven by the application’s domain needs and the desired granularity of concurrency control.

Differences Between TimeStamp and ConcurrencyCheck Attribute in EF Core:

In Entity Framework Core, the [Timestamp] and [ConcurrencyCheck] attributes relate to optimistic concurrency control but 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 originally retrieved when the entity was loaded. If the values don’t match, another process updates 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.

Key Differences:

Data Type:
Timestamp: Applied to a byte[] property in your model. This property generally corresponds to a RowVersion column in SQL Server or its equivalent in other databases.
ConcurrencyCheck: Can be applied to any property of an entity, not limited to a specific data type.

Database Behavior:
Timestamp: In databases like SQL Server, columns mapped from properties marked with Timestamp are automatically updated with a new value every time there is an update to the row. The database handles this auto-increment behavior.
ConcurrencyCheck: This does not automatically change the value. Instead, the property’s current value is checked against the database value during an update or delete operation.

Granularity:
Timestamp: Provides a single point of truth for concurrency checks. If any part of the row changes, the timestamp (RowVersion) value will change.
ConcurrencyCheck: Offers fine-grained control. Developers can specify which individual properties should be considered for concurrency checks.

Conflict Detection:
Timestamp: Conflicts are detected if the stored RowVersion value differs from the one the application provides during an update or deletion.
ConcurrencyCheck: Conflicts are detected if the stored value of any property marked with ConcurrencyCheck differs from the current value the application provides.

Storage Overhead:
Timestamp: Requires an additional RowVersion column in the database, which consumes 8 bytes of storage.
ConcurrencyCheck: Does not introduce additional columns. However, the properties you mark can be of any size or type, possibly leading to larger WHERE clauses in update/delete statements.

Applicability:
Timestamp: Typically applied once per entity to handle concurrency for the entire entity.
ConcurrencyCheck: This can be applied to multiple properties within an entity, each acting as a separate point of concurrency check.

Flexibility and Semantics:
Timestamp: The value is purely for concurrency control and doesn’t hold domain-specific semantics.
ConcurrencyCheck: The properties marked often have domain significance. Their primary purpose isn’t just concurrency control; they are used in that capacity when marked.

While both attributes address optimistic concurrency in EF Core, they have distinct behaviors and use cases. The Timestamp attribute offers a broad, automatic approach, while the ConcurrencyCheck attribute provides more granularity and control. The choice between the two often depends on the specific requirements of the application and the desired level of concurrency management.

In the next article, I will discuss Fluent API in Entity Framework Core with Examples. In this article, I try to explain the ConcurrencyCheck Data Annotation Attribute in Entity Framework Core with Examples. I hope you enjoyed this ConcurrencyCheck Attribute in EF Core with Examples article.

Leave a Reply

Your email address will not be published. Required fields are marked *