Optimistic concurrency: IsConcurrencyToken and RowVersion

Both in EF6 and EF-core, when working with SQL Server, you have to use this mapping:

modelBuilder.Entity<Product>() 
    .Property(t => t.RowVersion) 
    .IsRowVersion(); // Not: IsConcurrencyToken

IsConcurrencyToken does configure a property as concurrency token, but (when using it for a byte[] property)

  • the data type is varbinary(max)
  • its value is always null if you don’t initialize it
  • its value is not auto-incremented when a record is updated.

IsRowVersion on the other hand,

  • has datatype rowversion (in SQL Server, or timestamp in earlier versions), so
  • its value is never null, and
  • its value is always auto-incremented when a record is updated.
  • and it automatically configures the property to be an optimistic concurrency token.

Now when you update a Car you’ll see two update statements:

DECLARE @p int
UPDATE [dbo].[Product]
SET @p = 0
WHERE (([Id] = @0) AND ([Rowversion] = @1))
SELECT [Rowversion]
FROM [dbo].[Product]
WHERE @@ROWCOUNT > 0 AND [Id] = @0

UPDATE [dbo].[Car]
SET ...

The first statement doesn’t update anything, but it increments the rowversion, and it will throw a concurrency exception if the rowversion was changed in-between.

The [System.ComponentModel.DataAnnotations.Schema.Timestamp] attribute is the data annotations equivalent of IsRowVersion():

[Timestamp]
public byte[] RowVersion { get; set; }

Note that the official documentation is not correct. It says that IsConcurrencyToken is the fluent equivalent of the [Timestamp] attribute. However, IsRowVersion is the equivalent.

Leave a Comment