A better way for optimistic concurrency in Entity Framework Code First
Hi there :) Here's the thing: I love Entity Framework. I know it has its limitations, but it's super fun to work with — or at least it has been for me ever since I started using it. More specifically, ever since Code First came out.
Code First is easy and quick to work with, and it's a more natural way for any software developer to approach database object creation. Microsoft also favors Code First: just take a look at Entity Framework Core — no Db First or Model First (thank God for that).
So let's assume that you use Code First (and why wouldn't you, after all, it's 2017) and you want to build in some optimistic concurrency into your application (I assume you know what that is, if you don't take a look at this post). Let's say you have a Person class:
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
If you want this entity to be optimistic concurrency handled, then you can create a new byte[] property like this:
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public byte[] RowVersion { get; set; }
}
And then configure in the OnModelCreating() of the context this property as the rowversion for optimistic concurrency like this:
public class MyContext : DbContext
{
//...
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
base.OnModelCreating( modelBuilder );
modelBuilder.Entity<Person>(p => p.RowVersion).IsRowVersion();
}
}
And you're done. But if you want to be really pro, you can put this into a separate EntityConfiguration class for Person:
public class PersonEntityTypeConfiguration : EntityTypeConfiguration<Person>
{
public PersonEntityTypeConfiguration()
{
this.Property(e=>e.RowVersion).IsRowVersion();
}
}
And then add this to the modelBuilder:
public class MyContext : DbContext
{
//...
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
base.OnModelCreating( modelBuilder );
modelBuilder.Configurations.Add<PersonEntityTypeConfiguration>();
}
}
And now you're really done. At least for the Person class. If you have other classes that should be handled like this, you have to repeat the process for every one of them. That's error-prone, time-consuming, makes code harder to read and maintain and I could probably find a couple of other excuses we like to bring up when we don't want to do some repetitive, boring task.
To make it more interesting, I decided that this should be a lot easier: there should be an interface, like IConcurrencyAwareEntity, which should have a byte[] property, like RowVersion. Kind of like this:
public interface IConcurrencyAwareEntity
{
byte[] RowVersion { get; set; }
}
And then every time you want a row versioned entity, you just implement this interface and EF should automatically pick up on this and make the RowVersion property an actual row version for optimistic concurrency checks.
Fortunately, there is a way to make EF pick up on things like this: conventions. There is a lot of them included in EF — they are partially responsible for making everything "just magically work". Like if you have an ID property (or Id, or XId, or XID, where X is the owner class name) this automatically becomes a primary key. So why couldn't a RowVersion byte[] property work the same way for optimistic concurrency?
So I went ahead and created my own Entity Framework Code First Convention to enable just that. It's not that big of a deal, you just have to inherit the right class:
public class ConcurrencyAwareEntityConvention : Convention
{
public ConcurrencyAwareEntityConvention()
{
this.Types<IConcurrencyAwareEntity>().Configure(
config => config.Property(e => e.RowVersion).IsRowVersion()
);
}
}
So this creates a convention that says whenever you encounter an entity that implements IConcurrencyAwareEntity, configure the RowVersion byte[] property as the row version and use that for optimistic concurrency checks.
And finally it's only a matter of adding this convention:
public class MyContext : DbContext
{
//...
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
base.OnModelCreating( modelBuilder );
modelBuilder.Conventions.Add( new ConcurrencyAwareEntityConvention() );
}
}
I have also created my own DbContext descendant that includes this convention automatically, but this might be a bit overkill. Especially since you can configure the context to look through the assembly for any convention classes and apply them to itself.
And it's done. Being under concurrency constraints requires a specific behavior that comes with an entity - so in my opinion, this should be represented with an interface anyway (or better yet, not at all — to be entirely honest, this rowversion thing has always sounded to me like a bit of an abstraction leaking).
That's what IConcurrencyAwareEntity does: it adds the required capabilities to a class so it can be concurrency handled. The convention is there to wire the whole thing up with EF and make it more awesome. You can check out the code at GitHub.
Question: how awesome is that? Answer: