Back to: ASP.NET Core Tutorials For Beginners and Professionals
TimeStamp Attribute in Entity Framework Core (EF Core)
In this article, I am going to discuss TimeStamp Data Annotation Attribute in Entity Framework Core (EF Core) with Examples. Please read our previous article, where we discussed DatabaseGenerated Attribute in Entity Framework Core with Examples.
TimeStamp Attribute in Entity Framework Core
The TimeStamp Data Annotation Attribute in Entity Framework Core can only be applied once to a byte array type property in an entity class. It creates a column with timestamp data type in the SQL Server database. EF Core will automatically use this Timestamp column in concurrency check on the UPDATE SQL 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 in EF Core, then you will see the following. As you can see, this class has a parameterless constructor.
So, using the Timestamp Attribute in Entity Framework Core is one of the ways 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 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 has been 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 that 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 us maintain the integrity of the database when multiple users are updating and deleting rows simultaneously.
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 change the data in the database.
Example to Understand Timestamp Attribute in EF Core:
Let us understand Timestamp Attribute in Entity Framework Core 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. The database will create the column with the TimeStamp data type.
using System.ComponentModel.DataAnnotations; namespace EFCoreCodeFirstDemo.Entities { public class Student { public int StudentId { get; set; } public string? FirstName { get; set; } public string? LastName { get; set; } //We cannot have more than Column with [TimeStamp] Attribute //One Table Can have only one TimeStamp Column [Timestamp] public byte[]? RowVersion { 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. The point that you need to remember is that the database autogenerates the TimeStamp value. So, we do not need to set the value for that property.
using EFCoreCodeFirstDemo.Entities; namespace EFCoreCodeFirstDemo { internal class Program { static void Main(string[] args) { try { using EFCoreDbContext context = new EFCoreDbContext(); 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(); } 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.
If you verify the database, then the RowVersion column should be created with the TimeStamp data type, as shown in the image below.
Now run the application and then verify the Students table. 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.
The timestamp 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 first name and last name 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 Student? studentId1 = context.Students.Find(1); if(studentId1 != null) { studentId1.FirstName = "First Name Updated"; studentId1.LastName = "Last Name Updated"; context.SaveChanges(); Console.WriteLine("Student Updated"); } 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 RowVersion column within the UPDATE SQL Statement. This is to handle the concurrency issues.
If you verify the database, the RowVersion column should be updated with the latest increment value whose Student whose Id is 1, as shown in the image below.
Handling Concurrency Issues with EF Core using TimeStamp Attribute:
RowVersion (Also known as Timestamp, they are the same thing) is a SQL column type that uses auto-generated binary numbers that are unique across that database and stamped on records. Any time a record is inserted or updated on a table with a row version, a new unique number is generated (in binary format) and given to that record. Again, the RowVersions are unique across that entire database, not just the table.
Let us understand how to handle concurrency issues with Entity Framework Core. 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, it will use the RowVersion column while updating, but that RowVersion value with the Method2 entity has already been modified by Method1. So, Method2 has a RowVersion value that does not exist anymore in the database, and hence the Method2 SaveChanges method is going to throw an exception showing concurrency issues.
If you verify the database, you will see the updated data with the updated RowVersion value for the student whose id is 1, as shown in the image below. 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.
In the next article, I am going to discuss ConcurrencyCheck Attribute in Entity Framework Core with Examples. In this article, I try to explain TimeStamp Data Annotation Attribute in Entity Framework Core with Examples. I hope you enjoyed this TimeStamp Attribute in EF Core with Examples article.