TimeStamp Attribute in Entity Framework

TimeStamp Attribute in Entity Framework with Examples

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

TimeStamp Attribute in Entity Framework:

The Timestamp Data Annotation Attribute in Entity Framework can only be applied once in an entity class to a byte array type property. It creates a column with timestamp data type in the SQL Server database. Entity Framework API automatically uses this Timestamp column in concurrency check on the UPDATE statement in the database. The TimeStamp attribute can only be applied to a property that is of byte[] array type. It cannot be applied to other data types. If you go to the definition of TimeStamp Attribute, then you will see the following. As you can see, this class has a parameterless constructor.

TimeStamp Attribute in Entity Framework

So, using the Timestamp Attribute in Entity Framework is one of the ways 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 Timestamp column. TimeStamp can also be referred to as RowVersion.

What is TimeStamp or RowVersion in SQL Server?

In SQL Server, ROWVERSION and TIMESTAMP are synonyms. The ROWVERSION is available since SQL Server 2005, while TIMESTAMP is deprecated and will be removed in a future version of SQL Server.

ROWVERSION (TIMESTAMP) is an incrementing 8-byte binary number, and it does not store any DateTime-related information. In SQL Server, ROWVERSION and TIMESTAMP data types represent automatically generated binary numbers, unique within the database. This helps maintain the integrity of the database when multiple users are updating and deleting rows at the same time.

If a table contains a ROWVERSION (or TIMESTAMP) column, any time a new row is inserted or any existing row is updated, the value of the ROWVERSION (or TIMESTAMP) column is set to the current ROWVERSION (or TIMESTAMP) value. This is true, even when an UPDATE statement does not result in any changes to the data in the database.

Example to Understand Timestamp Attribute in Entity Framework:

Let us understand Timestamp Attribute in Entity Framework with an example. Please modify the Student Entity class as follows. Here, you can see, we have applied the TimeStamp Attribute of the RowVersion Property which is of byte[] array type. In an Entity, we can only apply the TimeStamp Attribute once. In the database, it will create the column with the TimeStamp data type.

using System.ComponentModel.DataAnnotations;
namespace EFCodeFirstDemo
{
    public class Student
    {
        public int StudentId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        [Timestamp]
        public byte[] RowVersion { get; set; }
        //We cannot have more than Column with [TimeStamp] Attribute
        //One Table Can have only one TimeStamp Column
    }
}

As we are going to update the Student Entity Model 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 DbSet.

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 or web.config file as shown in the below image.

TimeStamp Attribute in Entity Framework 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. The point that you need to remember the TimeStamp value is autogenerated by the data. So, we do not need to set the value for those properties.

using System;
namespace EFCodeFirstDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            using (EFCodeFirstContext context = new EFCodeFirstContext())
            {
                var student1 = new Student() { FirstName = "Pranaya", LastName = "Kumar" };
                context.Students.Add(student1);
                
                var student2 = new Student() { FirstName = "Hina", LastName = "Sharma" };
                context.Students.Add(student2);
                
                context.SaveChanges();
                Console.WriteLine("Students Added");
                Console.ReadKey();
            }
        }
    }
}

With the above changes in place, run the application and verify the database. The RowVersion column should be created with the TimeStamp data type as shown in the below image.

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

Now, if you verify the Students table, then you will see that the auto-incrementing 8-byte binary number generated, unique within the database will be stored in the RowVersion column as shown in the below image.

TimeStamp Data Annotation Attribute in Entity Framework Code First Approach

The timestamp column will be included in the where clause whenever you update an entity and call the SaveChanges method. We have already added two students with StudentId 1 and 2. Let us update the FirstName and LastName 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.FirstName = "First Name Updated";
                studentId1.LastName = "Last Name Updated";
                context.SaveChanges();

                Console.WriteLine("Student Updated");
                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 RowVersion column. This is to handle the concurrency issues.

TimeStamp Data Annotation Attribute in Entity Framework

Now, if you verify the database, then the RowVersion column should be updated with the latest increment value whose Student whose Id is 1 as shown in the below image.

TimeStamp Data Annotation Attribute

Handling Concurrency Issues with Entity Framework using TimeStamp Attribute:

Let us understand how to handle concurrency issues with Entity Framework. Please modify the Main Method of the Program class as follows. Here, we are updating the same entity 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 RowVersion value. Let us assume Method 1 starts updating first. So, he will update the data in the database and also update the RowVersion value. Now, Method2 tries to update the same entity. If you remember while updating it will use the RowVersion column, but that RowVersion value what with the Method2 entity has already been modified by Method1. So, Method2 has RowVersion value which does not exist anymore in the database, and hence the Method2 SaveChanges method going to throw an exception showing concurrency issues.

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.FirstName = studentId1.FirstName + " Method1";
                studentId1.LastName = studentId1.LastName + " 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.FirstName = studentId1.FirstName + " Method2";
                studentId1.LastName = studentId1.LastName + " Method2";
                context.SaveChanges();

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

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

Handling Concurrency Issues with Entity Framework using TimeStamp Attribute

Now, if you verify the database, then you will see the updated with updated RowVersion value for the student whose id is 1 as shown in the below image. So, in this way, TimeStamp Attribute handles the concurrency issues. Each time, you perform an INSERT or UPDATE Operation, the RowVersion value is going to increment.

Handling Concurrency Issues with Entity Framework using TimeStamp Attribute

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 TimeStamp Data Annotation Attribute in Entity Framework Code First Approach with Examples. I hope you enjoyed this TimeStamp 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 *