【问题标题】:EF core entity Id as custom class with identity columnEF 核心实体 ID 作为具有标识列的自定义类
【发布时间】:2020-08-05 08:19:05
【问题描述】:

我想在我的实体模型中使用强类型 ID,映射到 DB 中的 auto-incremented int(例如 MS SQL Identity 列)。但 EF Core 3.1 不支持自定义类型的标识:

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();
} 

使用这个模型我得到了例外:

身份值生成不能用于实体类型“供应商”上的属性“Id”,因为该属性类型是“供应商Id”。标识值生成只能与有符号整数属性一起使用。

我还尝试映射到私有整数支持字段int id,但是我无法在 EF 查询中使用 Id 属性。所以它看起来不是一个解决方案。

当我尝试生成 HiLo 密钥时也会遇到类似的问题。

你有什么想法可以解决这个问题吗?

【问题讨论】:

  • “但 EF Core 3.1 不支持自定义类型的标识” - 所以你知道目前没有解决方案。通常停止混合存储和域模型 - EFC 实体代表存储模型,并且需要简单的类型和属性而无需业务逻辑。如果您想在域模型中遵循 DDD 原则,请创建单独的域和存储模型,并在需要时在两者之间进行映射。
  • 当然@IvanStoev,我经常这样做。但是随着 EF Core 的新功能,我正在考虑开始对域和持久性使用相同的模型。如果我们忽略这个相当大的问题,简单的模型似乎是可能的。它应该简化许多不复杂功能的开发,而不会污染领域模型。当然,对于复杂的模型,隔离持久性模型总是更容易和更清晰。

标签: c# entity-framework entity-framework-core


【解决方案1】:

我使用 c# 9 记录的解决方案(在 ef core 5 中):

public abstract record StronglyTypedId<TValue>(TValue Value) where TValue : notnull
{
    public override string ToString() => Value.ToString();
}

使用抽象实体模型:

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}); }
    }
 
}

然后是一个示例实体模型

using TId = Int32;

public record EntityTypeId(TId Value) : StronglyTypedId<TId>(Value);

public class EntityName<EntityTypeId, TId>
{
    private EntityName() {} //for ef core
}

然后在模型构建器中(也可以是抽象/基础模型构建器)

    builder.Ignore(e => e.Id);
    builder
        .Property("_Id")
        .HasColumnName("Id")
        .UseIdentityColumn();
    builder.HasKey("_Id");

这确实意味着当从数据库(通过命令或存储库)检索时,您需要使用名称,例如

public async Task<EntityName> GetEntity(EntityTypeId typedId)
{
var entity = await context.EntityName
    .FirstOrDefaultAsync(x => EF.Property<int>(x, "_Id") == typedId.Value);
return entity;
}

不完美但有效

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-25
    • 2021-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多