Vinicius Gonçalves Vinicius Gonçalves - 3 months ago 67
C# Question

Optimistic concurrency: IsConcurrencyToken and RowVersion

I'm creating the default concurrency strategy that I will use in my application.

I decided for an optimistic strategy.

All of my entities are mapped as

Table per Type (TPT)
(using inheritance). I soon learned that there is a problem when using columns of type RowVersion with inheritance on Entity Framework:

Product

Id INT IDENTITY PRIMARY KEY
RowVersion ROWVERSION

Car (inherits Product records)

Color TYNIINT NOT NULL,
AnotherProperty....   


If I update a record of the
Car
table the RowVersion column from
Product
table will not be updated.

I plan to use a column of type
datetime2 (7)
in
Product
and update it manually if any records of the tables that inherit this table are modified.

I think I'm reinventing the wheel.

Is there another way to use the optimistic concurrency strategy with
ROWVERSION
when using
Table per Type (TPT)
in Entity Framework?

Edit

My mapping:

class Product
{
int Id { get; set; }
string Name { get; set; }
byte[] RowVersion { get; set; }
}

class Car : Product
{
int Color { get; set; }
}


CodeFirst conventions.

Only the RowVersion property on
Product
entity has custom definitions:

modelBuilder.Entity<Product>()
.Property(t => t.RowVersion)
.IsConcurrencyToken();

Answer

You have to use the 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 nvarchar(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 changed in-between.

By the way, IsRowVersion is equivalent to the [Timestamp] attribute:

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