Entity Framework Core 继承的外键命名问题

问题描述 投票:0回答:1

在为 Person 类层次结构创建迁移时,我遇到了 Entity Framework Core 的意外行为。具体来说,当我为 Person 类生成迁移时,我在 Person 表中得到一个名为 Student_InstitutionId 而不是 HospitalId 的外键。

public abstract class Person : Aggregate<long>
{
    // ... properties ...
}

public class Student : Person
{
    // ... properties ...
    public long InstitutionId { get; private set; }
    public virtual Institution Institution { get; set; }
}

public class Institution : Aggregate<long>
{
    // ... properties ...
    public virtual ICollection<Student> Students { get; set; }
}

我的Fluent API配置如下:

public class PersonEntityConfig : BaseEntityConfig<Person, long>
{
    public override void Configure(EntityTypeBuilder<Person> builder)
    {
        // ... configuration ...
    }
}

public class StudentEntityConfig : BaseEntityConfig<Student, long>
{
    public override void Configure(EntityTypeBuilder<Student> builder)
    {
        // ... configuration ...
        builder.HasOne(a => a.Institution)
               .WithMany(a => a.Students)
               .HasForeignKey(a => a.InstitutionId)
               .OnDelete(DeleteBehavior.NoAction);
    }
}
public class InstitutionEntityConfig : BaseEntityConfig<Institution, long>
{
    #region Fields
    #endregion

    #region Properties
    #endregion

    #region Methods
    public override void Configure(EntityTypeBuilder<Institution> builder)
    {
        base.Configure(builder);
        builder.Property(x => x.Name).HasMaxLength(50).IsRequired();
        builder.Property(x => x.CenterNumber).HasMaxLength(50);
        builder.Property(x => x.EMISNumber).HasMaxLength(50);
        builder.Property(x => x.EmailAddress).HasMaxLength(50);

        builder.HasOne(a => a.Address)
            .WithOne(i => i.Institution)
            .HasForeignKey<Institution>(x => x.AddressId)
            .OnDelete(DeleteBehavior.Cascade);
    }
    #endregion

    #region Constructors
    #endregion
}

该问题似乎与 Entity Framework Core 如何处理 Person 表中的鉴别器属性和外键有关。如何确保外键正确命名为InstitutionId而不是Student_InstitutionId?

我希望Person表中的外键命名为InstitutionId,而不是Student_InstitutionId。 生成迁移时,Entity Framework Core 会在 Person 表中创建一个名为 Student_InstitutionId 的外键。

请提供有关如何解决此命名问题的指导,并确保外键正确命名为InstitutionId。

c# entity-framework entity-framework-core
1个回答
0
投票

经过测试后发现,不,如果某些继承类具有关系而其他类没有关系,那么对于 TPH 继承结构、代码优先或模式优先,这将无法开箱即用。使用以下示例:

[Table("Animals")]
public abstract class Animal
{
    [Key]
    public int AnimalId { get; set; }
    public string Name { get; set; }
}

public class Dog : Animal
{
    public virtual Owner Owner { get; set; }
}

public class Cat : Animal
{
    public virtual Owner Owner { get; set; }
}

public class Wolf : Animal
{
}

配置:

        modelBuilder.Entity<Animal>()
            .HasDiscriminator<string>("AnimalType")
            .HasValue<Dog>("Dog")
            .HasValue<Cat>("Cat")
            .HasValue<Wolf>("Wolf");
        modelBuilder.Entity<Dog>()
            .HasOne(x => x.Owner)
            .WithMany()
            .IsRequired(false)
            .HasForeignKey("OwnerId")
            .HasConstraintName("FK_Dogs_Owners");
        modelBuilder.Entity<Cat>()
            .HasOne(x => x.Owner)
            .WithMany()
            .HasForeignKey("OwnerId")
            .HasConstraintName("FK_Cats_Owners");

如果我们想要猫狗的主人而不是狼的主人,那么即使尝试用单独的 FK 约束来“欺骗”它也是行不通的。它期望 Animal 表上有 Dog_OwnerId 和 Cat_OwnerId。 如果所有动物都有主人,这很好,我们可以将主人放在动物中,一切正常,但狼有主人。不管怎样,无论我们使用单个 OwnerId 还是 DogOwnerId/CatOwnerId,这些 FK 都需要可以为空,如果猫和狗需要一个所有者,那么从引用完整性 PoV 来看,这可能并不理想。对于 TPH,我们需要在 Animal 表中包含 DogOwnerId 和 CatOwnerId,否则我们可以稍微“破解”它:

[Table("Animals")]
public abstract class Animal
{
    [Key]
    public int AnimalId { get; set; }
    public string Name { get; set; }

    public virtual Owner? Owner { get; set; }

}

public class Dog : Animal
{
}

public class Cat : Animal
{
}

public class Wolf : Animal
{
    public new virtual Owner? Owner { get { return null; }; set { if (value != null) throw new InvalidOperationException("Wolves have no owner."); } }
}

这将 Owner 移至 Animal 以使 FK 关联满意,但我们在 Wolf 中重载了 Owner,因此如果有任何东西试图设置所有者,我们会抛出异常。这不会阻止数据库为狼分配所有者,并且 EF 甚至会很乐意按所有者查询狼。

如果某些继承类具有关系而其他类没有关系,并且要正确强制引用完整性,更好的解决方案是切换到 TPT 继承。在这里,我们将删除 Animal 表的 OwnerId 和 Discriminator。我们添加一个 Dog 表,AnimalId 为 PK + FK 为 Animal,OwnerId FK 为 Owner。我们将 AnimalId 为 PK + FK 的 Cat 表添加到 Animal,将 OwnerId FK 添加到 Owner。我们将包含 Animal 的 Wolf 表作为 PK + FK 添加到 Animal,没有 OwnerId。可以稍微更新实体的表名称:

[Table("Animals")]
public abstract class Animal
{
    [Key]
    public int AnimalId { get; set; }
    public string Name { get; set; }
}

[Table("Dogs")]
public class Dog : Animal
{
    public virtual Owner Owner { get; set; }
}

[Table("Cats")]
public class Cat : Animal
{
    public virtual Owner Owner { get; set; }
}

[Table("Wolves")]
public class Wolf : Animal
{
}

...以及映射:

        modelBuilder.Entity<Animal>()
            .ToTable("Animals")
        modelBuilder.Entity<Dog>()
            .HasOne(x => x.Owner)
            .WithMany()
            .HasForeignKey("OwnerId");
        modelBuilder.Entity<Cat>()
            .HasOne(x => x.Owner)
            .WithMany()
            .HasForeignKey("OwnerId");

这里的问题是我们可以对子类表使用

[Table("Dogs")]
属性,但我们仍然需要对基类显式
.ToTable("Animals")
。这似乎是 TPT 得到认可的必要条件。仅使用
[Table]
属性似乎让 EF 期望使用 TPC 继承。

© www.soinside.com 2019 - 2024. All rights reserved.