Best way to handle optimistic concurrency with Entity Framework 7

asp.net-core entity-framework-core

Question

I am using ASP.NET 5 rc1 and Entity 7 rc1 over coreclr with a sample code first-migration project.

I was having a look at how to manage concurrency in entities but I couldn't find any good updated information about the recommended concurrency handling practices for Entity 7.

I have a few entities, they all implement the interface:

public interface IVersionedModel
{
    byte [] RowVersion { get; set; }
}

as for example in:

public class User : IVersionedModel
{
    public Guid UserId { get; set; }
    public string Name { get; set; }
    public byte[] RowVersion { get; set; }
}

Instead adding the attribute [Timestamp] to each RowVersion column I prefer to use the fluent configuration, so in my DbContext I specify for each entity that I want that column to be handled as the row version.

However Entity 7 does not seem to support anymore the option .IsRowVersion() but it has the option IsConcurrencyToken(), which should suffice but in code first migrations it generates a nullable varbinary column and when inserting/updating rows it does not auto-increment.

I wonder whether the best thing to do is to explicitly manage the version of each row by increasing the version when updating or inserting entities in the database.. This is my DbContext

public class AppDbContext : DbContext
{
    public AppDbContext() : base()
    {
    }

    public DbSet<User> Users { get; set; }
    public DbSet<Organization> Organizations { get; set; }
    public DbSet<Membership> Memberships { get; set; }

    public override int SaveChanges()
    {
        //increase entity version for concurrency purposes
        foreach(var dbEntityEntry in ChangeTracker.Entries()
            .Where(x => x.State == EntityState.Added || x.State == EntityState.Modified))
        {
            IVersionedModel entity = dbEntityEntry.Entity as IVersionedModel;
            if(entity != null)
            {
                //Increase byte[] RowVersion?
            }
        }
        return base.SaveChanges();
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>().Property(u => u.RowVersion).IsConcurrencyToken();
        modelBuilder.Entity<Organization>().Property(o => o.RowVersion).IsConcurrencyToken();
        modelBuilder.Entity<Membership>().Property(m => m.RowVersion).IsConcurrencyToken();
    }
}

Basically my question is, how to handle concurrency with ASP.NET 5 and Entity 7? It's the first time I deal with this thing and any suggestion will be appreciated.

UPDATE: Thanks to the link https://stackoverflow.com/users/1922568/joe-audette provided I found some answers, unfortunately not all

As per http://docs.efproject.net/en/latest/modeling/concurrency.html#how-concurrency-tokens-work-in-ef it seems we can use Timestamp annotation if we want to version a row or ConcurrencyCheck annotation on a property if we want to version based on a property (not the whole row).

The fluent API only allows to configure concurrency token by using .IsConcurrencyToken()

I haven't found yet a way through the fluent API to version the whole row with a dedicated column (it'd be nice if the fluent API allowed an .IsRowVersion(), but it doesn't)

Popular Answer

I've created a static class to handle this and other missing features of EF7 ModelBuilder (use with caution, not tested in a production environment)

public static class SqlServerModelBuilderExtensions
{
    public static PropertyBuilder<decimal?> HasPrecision(this PropertyBuilder<decimal?> builder, int precision, int scale)
    {
        return builder.HasColumnType($"decimal({precision},{scale})");
    }

    public static PropertyBuilder<decimal> HasPrecision(this PropertyBuilder<decimal> builder, int precision, int scale)
    {
        return builder.HasColumnType($"decimal({precision},{scale})");
    }

    public static PropertyBuilder<byte[]> IsRowVersion(this PropertyBuilder<byte[]> builder)
    {
        return builder.HasColumnType("rowversion").IsConcurrencyToken().ValueGeneratedOnAdd();
    }
}


Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why