ConcurrencyCheck Attribute in Entity Framework

ConcurrencyCheck Attribute in Entity Framework

In this article, I am going to discuss ConcurrencyCheck Data Annotation Attribute in Entity Framework Code First Approach with Examples. Please read our previous article where we discussed TimeStamp Data Annotation Attribute in Entity Framework Code First Approach with Examples.

ConcurrencyCheck Attribute in Entity Framework

The ConcurrencyCheck Data Annotation Attribute can be applied to one or more properties of an entity class in Entity Framework. When we applied 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 is another way to handle the Concurrency Issues. The Concurrency issue arises when multiple users or multiple transactions attempt to update/delete the same row at the same time. We can handle this Concurrency issue using the ConcurrencyCheck column.

Example to Understand ConcurrencyCheck Attribute in Entity Framework:

Let us understand ConcurrencyCheck Data Annotation Attribute in Entity Framework Code First Approach with an example. Please modify the Student Entity class as follows. Here, you can see, we have applied the ConcurrencyCheck Attribute of the Name Property. In an Entity, we can only apply the ConcurrencyCheck Attribute with one or more properties.

using System.ComponentModel.DataAnnotations;
namespace EFCodeFirstDemo
{
    public class Student
    {
        public int StudentId { get; set; }
        public int RegdNumber { get; set; }
        [ConcurrencyCheck]
        public string Name { get; set; }
        public string Branch { get; set; }
    }
}

Here, we have applied the ConcurrencyCheck on the Name property of the Student class. So, Entity Framework API will include the Name column in the UPDATE statement to check for optimistic concurrency.

As we are going to update the Student Entity class many times, in order to avoid the Run-Time Exception when the model changes and when we rerun the application, let us set the database initializer as DropCreateDatabaseIfModelChanges. So, modify the context class as follows. As you can see here, we have registered the Student model within the context class using the DbSet type.

using System.Data.Entity;
namespace EFCodeFirstDemo
{
    public class EFCodeFirstContext : DbContext
    {
        public EFCodeFirstContext() : base("name=MyConnectionString")
        {
            //Setting the Database Initializer as DropCreateDatabaseIfModelChanges
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<EFCodeFirstContext>());
        }

        public DbSet<Student> Students { get; set; }
    }
}

Please make sure to have the connection string with the name MyConnectionString within the app.config file as shown in the below image.

ConcurrencyCheck Data Annotation Attribute in Entity Framework Code First Approach with Examples

Next, modify the Main Method of the Program class as follows. Here, you can see, we have added two entities to the database.

using System;
namespace EFCodeFirstDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            using (EFCodeFirstContext context = new EFCodeFirstContext())
            {
                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();
            }
            Console.ReadKey();
        }
    }
}

With the above changes in place, run the application and verify the database and you should see the following.

ConcurrencyCheck Data Annotation Attribute in Entity Framework Code First Approach

Now, if you verify the Students table, then you will see the following two records.

ConcurrencyCheck Data Annotation Attribute in Entity Framework

The ConcurrencyCheck column will be included in the where clause whenever we update 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 System;
namespace EFCodeFirstDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            using (EFCodeFirstContext context = new EFCodeFirstContext())
            {
                //Fetch the Student Details whose Id is 1
                var studentId1 = context.Students.Find(1);

                //Log the Generated SQL in the Console
                context.Database.Log = Console.Write;
                studentId1.Name = "Name Updated";
                studentId1.Branch = "Branch Updated";
                context.SaveChanges();

                Console.WriteLine("Student Updated");
                Console.ReadKey();
            }
            Console.ReadKey();
        }
    }
}

Now, 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 column. This is to handle the concurrency issues.

ConcurrencyCheck Data Annotation Attribute

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 at the same time. Both Method1 and Method2 reads the student entity whose Id is 1 and also read the same Name value. Let us assume Method1 starts updating first. So, he will update the data in the database and also update the Name Column value. Now, Method2 tries to update the same entity. If you remember while updating, it will use the Name column in the where clause, but that Name column value what with the Method2 entity has already been modified by Method1. So, Method2 has a Name value that does not exist anymore in the database and hence Method2 SaveChanges method 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 System;
using System.Threading;
namespace EFCodeFirstDemo
{
    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");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception Occurred: {ex.Message}");
            }

            Console.ReadKey();
        }

        public static void Method1()
        {
            using (EFCodeFirstContext context = new EFCodeFirstContext())
            {
                //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));

                studentId1.Name = studentId1.Name + " Method1";
                studentId1.Branch = studentId1.Branch + " Method1";
                context.SaveChanges();

                Console.WriteLine("Student Updated by Method1");
            }
        }

        public static void Method2()
        {
            using (EFCodeFirstContext context = new EFCodeFirstContext())
            {
                //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));

                studentId1.Name = studentId1.Name + " Method2";
                studentId1.Branch = studentId1.Branch + " Method2";
                context.SaveChanges();

                Console.WriteLine("Student Updated by Method2");
            }
        }
    }
}

When you run the above application, you will get the following exception and this makes sense.

Handling Concurrency Issues using ConcurrencyCheck in Entity Framework

Note: The Timestamp Attribute can only be applied to a single-byte array property, whereas the ConcurrencyCheck attribute can be applied to any number of properties with any data type.

In the next article, I am going to discuss Fluent API in Entity Framework Code First Approach with Examples. Here, in this article, I try to explain ConcurrencyCheck Data Annotation Attribute in Entity Framework Code First Approach with Examples. I hope you enjoyed this ConcurrencyCheck Attribute in Entity Framework Code First Approach with Examples article. Please give your valuable feedback and suggestions about this article.

Leave a Reply

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