ef-core-3.0

IsConcurrencyToken(true) does not actually check concurrency tokens


I have a property configured like this:

public byte[] Timestamp { get; set; }

And then in my DbContext, i use the Fluent API like so:

modelBuilder.Entity<MyClass>()
.Property(x => x.Timestamp)
.IsRowVersion()
.IsConcurrencyToken(true);

So naturally i went ahead and wrote a unit test, ensuring that an entity with a wrong timestamp set would not get saved. I used Sqlite and some custom Sql to make RowVersion work in Unit Tests but to my surprise i never got an exception. Then, i tested it in our application, and i also did not get an exception when a wrong Timestamp was set on the entity.

var myInstance = await myDbContext.Instances
.Include(x => x...)
.Include(x => x...)
.SingleAsync(x => x.Id == id);

// set other values, add new entities to relationships, aso

myInstance.Timestamp = new byte[] { 1, 2, 3, 4 };

await myDbContext.SaveChangesAsync();

I am clearly missing something here. I thought configuring IsRowVersion would be enough to force EF Core to include a WHERE Timestamp = clause in the UPDATE but it seems like thats not the case. As you can see, i also tried calling IsConcurrencyToken (even with its default value of true, just to be sure) but to no avail.

Edit: I have worked "around" it now by including the Timestamp in my SingleAsync call, but this still leaves me unsure if its still possible to not get a concurrency exception, as the Timestamp set on my entity is apparently not checked at all when saving?


Solution

  • This is a known behavior of EF core, as documented here:

    https://github.com/aspnet/EntityFrameworkCore/issues/18505

    Manually changing the value of the token is considered to be a no-op, as the original value that was queried from the database is being used for a concurrency check. Manually changing the value does nothing, as it is effectively being ignored.