Natural identifiers in your domain (with Entity Framework Core) - generated by database
Part 2 is about configuring EF Core with databse side generated identifiers, but there is also a part 1 where a configuration for server side generated identifiers is explained
This is a continuation of this article
How to configure it in Entity Framework Core?
Described solution is working fine on EF Core 3.0
Configuration of Order class in EF Core:
public class OrderId {
public long Value { get; private set; }
}
public class Order {
public OrderId Id { get; private set; }
[...]
}
public class OrderConfiguration : IEntityTypeConfiguration<Order> {
public void Configure(EntityTypeBuilder<Order> builder)
{
builder.ToTable(nameof(Order));
builder.HasKey(order => order.Id);
builder.Property(order => order.Id)
.ValueGeneratedOnAdd();
}
}
CREATE TABLE [dbo].[Order] (
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[...]
)
There are few things that needs to be done before EF Core understands how to handle our identities correctly:
- Identifiers have to implement following methods / interfaces:
Equals/GetHashCodeIComparable== / !=operators
- Custom implementation for
IValueConverterSelectorandValueConverter<TIdentifier, TType>with converter mapping hints that returns temporary identifiers and allows EF Core to track entities - Replacing internal EF Core implementation of
IValueConverterSelectorwith our custom implementation
After all those changes EF Core will be able to understand LinQ queries with our identities and translates it correctly to SQL queries (will be evaluated on SQL with WHERE clause).
public class OrderService : IOrderService {
public Order GetOrder(OrderId id) {
return dbContext.Orders.SingleOrDefault(c => c.Id == id);
}
}
EF Core migrations
To bring back EF Core migrations with support for natural identifiers you need to do this:
- Implement custom
UseIdentityColumnmethod and store annotation forSqlServerValueGenerationStrategy.IdentityColumnunder custom name to avoid EF Core type validations fromMicrosoft.EntityFrameworkCore.SqlServerPropertyExtensions.CheckValueGenerationStrategy - Override
SqlServerMigrationsAnnotationProviderand read your custom annotation from point above, when you find them, add an annotation for property with nameSqlServerAnnotationNames.Identity
After those points, every time when you will generate new migrations by EF Core mechanism, it will generate correct annotations and allows you to use database as a source for values of ID, e.g.:
migrationBuilder.CreateTable(
name: "Order",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Description = table.Column<string>(nullable: true),
Customer_FirstName = table.Column<string>(nullable: true),
Customer_LastName = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Order", x => x.Id);
});
Code with examples could be found on my GitHub