未定义 EF Core 迁移基本类型密钥

EF Core Migration base type key not defined

我设置了一组继承自 'base' 实体的实体,其中包含关键字段和 2 个日期时间审计字段:

/// <summary>
/// The <see cref="BaseEntity"/> class is a standard entity from which all other entities inherit.
/// </summary>
public abstract class BaseEntity
{
    /// <summary>
    /// Universal unique identifier for the entity.
    /// </summary>
    public Guid Guid { get; set; }

    /// <summary>
    /// Timestamp for when the entity was created.
    /// </summary>
    public DateTime CreatedAtTime { get; set; }

    /// <summary>
    /// Timestamp for when the entity was last updated.
    /// </summary>
    public DateTime UpdatedAtTime { get; set; }
}

我已经设置了一个继承这个抽象的实体 class:

/// <summary>
/// A <see cref="MilitaryUnit"/> is a group of <see cref="MilitaryMember"/>'s that work together
/// and have a 'chain of command'
/// </summary>
public class MilitaryUnit : BaseEntity
{
    public string Name { get; set; }

    public string Description { get; set; }

    public virtual ICollection<MilitaryMember> Members { get; set; }

    public virtual ICollection<MilitaryUnitSection> Sections { get; set; }

    public MilitaryUnit()
    {
        this.Members = new HashSet<MilitaryMember>();
        this.Sections = new HashSet<MilitaryUnitSection>();
    }
}

在我的 DbContext 中,我创建了一个引用 'MilitaryUnit' 实体的 DbSet,并且我应用了配置:

DbContext

public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { }

public DbSet<MilitaryUnit> MilitaryUnits { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.ApplyConfiguration<MilitaryUnit>(new MilitaryUnitConfiguration());
}

军事单位配置

public class MilitaryUnitConfiguration : IEntityTypeConfiguration<MilitaryUnit>
{
    public void Configure(EntityTypeBuilder<MilitaryUnit> builder)
    {
        // All entities inherit from the BaseEntity type
        builder.HasBaseType<BaseEntity>();

        // The unit name can only be 50 characters long and is unique
        builder.Property(entity => entity.Name)
                .HasColumnType("varchar(50)")
                .HasMaxLength(50)
                .IsRequired();

        builder.HasAlternateKey(entity => entity.Name);

        // The unit has a description that can be up to 100 character long
        builder.Property(entity => entity.Description)
                .HasColumnType("varchar(100)")
                .HasMaxLength(100);

        // The unit has multiple members
        builder.HasMany<MilitaryMember>(entity => entity.Members);

        // The unit has multiple sections
        builder.HasMany<MilitaryUnitSection>(entity => entity.Sections);
    }
}

当我尝试应用迁移时出现以下错误:

A key cannot be configured on 'MilitaryUnit' because it is a derived type. The key must be configured on the root type 'BaseEntity'. If you did not intend for 'BaseEntity' to be included in the model, ensure that it is not included in a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation property on a type that is included in the model.

现在我有 'BaseEntity' 的模型配置:

public class BaseEntityConfiguration : IEntityTypeConfiguration<BaseEntity>
{
    public void Configure(EntityTypeBuilder<BaseEntity> builder)
    {
        builder.HasKey(entity => entity.Guid);

        builder.Property(entity => entity.Guid)
                .HasColumnType("guid");

        builder.Property(entity => entity.CreatedAtTime)
                .HasColumnType("datetime")
                .HasValueGenerator(typeof(CurrentDateTimeGenerator))
                .ValueGeneratedOnAdd();

        // The updated timestamp has a default value of the minimum date time value and will only
        // generate a new date time when the entity has been updated
        builder.Property(entity => entity.UpdatedAtTime)
                .HasColumnType("datetime")
                .HasDefaultValue(DateTime.MinValue)
                .HasValueGenerator(typeof(CurrentDateTimeGenerator))
                .ValueGeneratedOnUpdate();
    }
}

...但我不确定在哪里应用它!我假设它在 DbContext 的某个地方,但在尝试这个之后我仍然得到错误(如上所述)。我快疯了,错过了一些显而易见的东西?

感谢大家的回复!所以看起来解决方案并不太可怕:

我将 BaseEntityConfiguration class 设置为一个抽象 class,它采用我想要配置的实体类型并实现 IEntityTypeConfiguration 接口,并使 Configure 方法能够 'overidable' .

基本配置

public abstract class BaseEntityConfiguration<TEntityType> : IEntityTypeConfiguration<TEntityType>
    where TEntityType : BaseEntity
{
    public virtual void Configure(EntityTypeBuilder<TEntityType> builder)
    {
        builder.HasKey(entity => entity.Guid);

        // The created timestamp has a default value of the current system time for when the entity
        // was created in the database. This value cannot be changed after it is set
        builder.Property(entity => entity.CreatedAtTime)
                .HasColumnType("datetime")
                .HasValueGenerator(typeof(CurrentDateTimeGenerator))
                .ValueGeneratedOnAdd();

        // The updated timestamp has a default value of the minimum date time value and will only
        // generate a new date time when the entity has been updated
        builder.Property(entity => entity.UpdatedAtTime)
                .HasColumnType("datetime")
                .HasDefaultValue(DateTime.MinValue)
                .HasValueGenerator(typeof(CurrentDateTimeGenerator))
                .ValueGeneratedOnUpdate();
    }
}

然后在实体配置上 classes 我扩展了这个 BaseEntityConfiguration class 并覆盖了 Configure 方法,同时从抽象 class:

public class MilitaryUnitConfiguration : BaseEntityConfiguration<MilitaryUnit>
{
    public override void Configure(EntityTypeBuilder<MilitaryUnit> builder)
    {
        base.Configure(builder);

        // The unit name can only be 50 characters long and is unique
        builder.Property(entity => entity.Name)
                .HasColumnType("varchar(50)")
                .HasMaxLength(50)
                .IsRequired();

        builder.HasAlternateKey(entity => entity.Name);

        // The unit has a description that can be up to 100 character long
        builder.Property(entity => entity.Description)
                .HasColumnType("varchar(100)")
                .HasMaxLength(100);

        // The unit has multiple members
        builder.HasMany<MilitaryMember>(entity => entity.Members);

        // The unit has multiple sections
        builder.HasMany<MilitaryUnitSection>(entity => entity.Sections);
    }
}

虽然我没有对此进行彻底测试,但似乎迁移已成功设置:

migrationBuilder.CreateTable(
    name: "MilitaryUnits",
    columns: table => new
    {
        Guid = table.Column<Guid>(nullable: false),
        CreatedAtTime = table.Column<DateTime>(type: "datetime", nullable: false),
        UpdatedAtTime = table.Column<DateTime>(type: "datetime", nullable: false, defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)),
        Name = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false),
        Description = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: true)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_MilitaryUnits", x => x.Guid);
        table.UniqueConstraint("AK_MilitaryUnits_Name", x => x.Name);
    });