Entity Framework Core 7: Strongly Typed Ids Together With Auto-Increment Columns

David Masters
2 min readOct 12, 2022
Strong dude flexing
Photo by KirstenMarie on Unsplash

Entity Framework Core has come a long way for supporting the persistence of DDD style entities. You can now have constructors on your types, read-only properties & collections backed with encapsulated private fields, and “owned” child entities that help to effectively implement aggregates with child entities & value objects.

Another DDD pattern you can implement is strongly-typed IDs, to combat primitive obsession. With the introduction of the record type in C#, this can be achieved with an ultra concise one-liner and no need to override the equality operator:

public readonly record struct CustomerId(Guid Value);

You can then configure EF core so it knows how to convert to and from the primitive value that will be stored in the database:

var entity = modelBuilder.Entity<Customer>();

entity.HasKey(e => e.Id);

entity.Property(e => e.Id)
.HasConversion(id => id.Value, value => new CustomerId(value));

Alternatively you can create a re-usable ValueConverter object; useful when configuring other entities that have foreign keys to the same type:

var converter = new ValueConverter<CustomerId, Guid>(
id => id.Value,
guid => new CustomerId(guid));

var entity = modelBuilder.Entity<Customer>();

entity.HasKey(e => e.Id);

entity.Property(e => e.Id)
.HasConversion(converter);

Blog posts from Thomas Levesque and Andrew Lock both do a good job of explaining this, including mechanisms to automatically add these conversions for all of your entities.

But one feature that’s missing from EF Core, is the ability to use these strongly-typed Ids in conjunction with a database’s auto increment feature.

As of EF Core 6, if you were to try the following:

public readonly record struct CustomerId(int Value);var entity = modelBuilder.Entity<Customer>();

entity.HasKey(e => e.Id);

entity.Property(e => e.Id)
.HasConversion(
id => id.Value,
intValue => new CustomerId(intValue));
.ValueGeneratedOnAdd();

Then you’d get the following exception:

Identity value generation cannot be used for the property ‘Id’ on entity type ‘Customer’ because the property type is ‘CustomerId’. Identity value generation can only be used with signed integer properties.

Given how common it is to use an auto-incrementing integer as a primary key, this is quite a big flaw.

Fortunately, this has been addressed for Entity Framework Core 7 (due for release November 2022). If you install the latest EF Core 7 release candidate, you will find the above configuration works great.

--

--

David Masters

Software developer on the south coast of the UK. Opinions expressed are solely my own and do not express the views or opinions of my employer.