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

时间:2018-11-30 23:46:10

标签: c# entity-framework ef-fluent-api

我已经设置了一组从“基本”实体继承的实体,该实体包含键字段和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; }
}

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

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

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

  

无法在“ MilitaryUnit”上配置密钥,因为它是派生的   类型。该密钥必须在根类型“ BaseEntity”上进行配置。如果你   不打算将“ BaseEntity”包含在模型中,请确保   它不包含在您的上下文的DbSet属性中,   在对ModelBuilder的配置调用中引用,或从   模型中包含的类型上的导航属性。

现在,我具有“ 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中的某个位置,但是尝试了之后,仍然出现错误(如上所述)。我要疯了,完全看不见什么东西?

1 个答案:

答案 0 :(得分:1)

感谢所有回复!因此,似乎解决方案并不太可怕:

我将BaseEntityConfiguration类设置为一个抽象类,该类采用我要配置的实体类型并实现IEntityTypeConfiguration接口,并使Configure方法能够“覆盖”。

BaseConfiguration

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

然后在实体配置类上,我扩展此BaseEntityConfiguration类并覆盖Configure方法,同时从抽象类执行基本的Configure方法:

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