Reputation: 162
I'd like to use strongly typed Ids in my entity model, mapped to auto-incremented int in DB (e.g. MS SQL Identity column). But EF Core 3.1 doesn't support the identity on custom type:
public class Vendor
{
public VendorId Id { get; private set; }
public string CompanyName { get; set; }
}
public class VendorId
{
public int Value { get; }
public VendorId(int value)
{
Value = value;
}
}
public void Configure(EntityTypeBuilder<Vendor> vendorEntity)
{
vendorEntity.HasKey(vendor => vendor.Id);
vendorEntity.Property(vendor => vendor.Id)
.HasConversion(vendorId => vendorId.Value, dbValue => new VendorId(dbValue))
.UseIdentityColumn();
}
With this model I get exception:
Identity value generation cannot be used for the property 'Id' on entity type 'Vendor' because the property type is 'VendorId'. Identity value generation can only be used with signed integer properties.
I also tried to map to private integer backing field int id
, but then I can't use the Id property in EF queries. So it doesn't looks to be a solution.
Similar problems are when I try HiLo key generation.
Do you have any ideas how to solve this problem?
Upvotes: 0
Views: 2102
Reputation: 11
the solution i've used (in ef core 5) using c# 9 records:
public abstract record StronglyTypedId<TValue>(TValue Value) where TValue : notnull
{
public override string ToString() => Value.ToString();
}
with an abstract entity model:
public abstract class Entity<TId, T> where TId: StronglyTypedId<T>
{
T _Id;
public virtual TId Id
{
get { return (TId)Activator.CreateInstance(typeof(TId), new object[] {_Id}); }
}
}
then an example entity model
using TId = Int32;
public record EntityTypeId(TId Value) : StronglyTypedId<TId>(Value);
public class EntityName<EntityTypeId, TId>
{
private EntityName() {} //for ef core
}
then in the model builder (which can again be an abstract / base model builder)
builder.Ignore(e => e.Id);
builder
.Property("_Id")
.HasColumnName("Id")
.UseIdentityColumn();
builder.HasKey("_Id");
this does mean that when retrieving from the database (via command or repository) you need to use the name e.g.
public async Task<EntityName> GetEntity(EntityTypeId typedId)
{
var entity = await context.EntityName
.FirstOrDefaultAsync(x => EF.Property<int>(x, "_Id") == typedId.Value);
return entity;
}
not perfect but works
Upvotes: 1