You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core TPT为何同时生成基类与派生类的外键?

问题:TPT模式下Ownership表生成多余外键的原因?

我有一个模型,其中Company和NaturalPerson均继承自Person(公司属于法人)。公司和自然人都可以拥有资产,因此我创建了Ownership对象来关联Person,该Person可以是Company或NaturalPerson。

使用TPT生成模型时,已生成Person、Company、NaturalPerson表,这部分正常。但Ownership表中,我预期仅存在指向Person表的1个外键,却额外生成了指向Company和NaturalPerson表的2个外键,总计3个。请问这是否正常?若正常,原因是什么?还是我配置出错了?


代码示例

public class MyContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Person>().ToTable("Person");
        modelBuilder.Entity<NaturalPerson>().ToTable("NaturalPerson");
        modelBuilder.Entity<LegalPerson>().ToTable("LegalPerson");
        modelBuilder.Entity<CompanyThatCannotOwnStuff>().ToTable("CompanyThatCannotOwnStuff");
        modelBuilder.Entity<Company>().ToTable("Company");
        modelBuilder.Entity<SharableGood>().ToTable("Good");
        modelBuilder.Entity<Ownership>()
            .HasOne(o => o.Owner)
            .WithMany()
            .HasForeignKey(o => o.OwnerId)
            .OnDelete(DeleteBehavior.NoAction);
        modelBuilder.Entity<Ownership>()
            .HasOne(o => o.SharableGood)
            .WithMany()
            .HasForeignKey(o => o.SharableGoodId)
            .OnDelete(DeleteBehavior.NoAction);
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
        optionsBuilder.UseSqlServer("Server=localhost;Database=Test;Trusted_Connection=True;");
    }
}

public abstract class Person
{
    public Guid Id { get; set; }
}

public class NaturalPerson : Person
{
    public List<Ownership> Ownerships { get; set; }
}

public class LegalPerson : Person { }

public class CompanyThatCannotOwnStuff : LegalPerson { }

public class Company: LegalPerson
{
    public List<Ownership> Ownerships { get; set; }
}

public class SharableGood
{
    public Guid Id { get; set; }
}

public class Ownership
{
    public Guid Id { get; set; }
    public Guid SharableGoodId { get; set; }
    public SharableGood SharableGood { get; set; }
    public Guid OwnerId { get; set; }
    public Person Owner { get; set; }
    public DateTime ImportantDate { get; set; }
}

生成的Ownership表迁移代码

migrationBuilder.CreateTable(
    name: "Ownership",
    columns: table => new
    {
        Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
        SharableGoodId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
        OwnerId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
        ImportantDate = table.Column<DateTime>(type: "datetime2", nullable: false),
        CompanyId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
        NaturalPersonId = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_Ownership", x => x.Id);
        table.ForeignKey(
            name: "FK_Ownership_Company_CompanyId",
            column: x => x.CompanyId,
            principalTable: "Company",
            principalColumn: "Id");
        table.ForeignKey(
            name: "FK_Ownership_Good_SharableGoodId",
            column: x => x.SharableGoodId,
            principalTable: "Good",
            principalColumn: "Id");
        table.ForeignKey(
            name: "FK_Ownership_NaturalPerson_NaturalPersonId",
            column: x => x.NaturalPersonId,
            principalTable: "NaturalPerson",
            principalColumn: "Id");
        table.ForeignKey(
            name: "FK_Ownership_Person_OwnerId",
            column: x => x.OwnerId,
            principalTable: "Person",
            principalColumn: "Id");
    });

解答

这是配置错误导致的,核心原因是派生类的导航属性未与Ownership.Owner正确关联,EF Core误生成了额外的独立关联。

问题根源

你的NaturalPerson和Company类中都定义了List<Ownership>导航属性,但在OnModelCreating中,你仅配置了Ownership.Owner到Person的关联(且WithMany()未指定对应导航属性),EF Core会默认将派生类中的Ownerships视为全新的、独立的关联关系,因此为每个派生类生成了单独的外键列(CompanyId、NaturalPersonId),再加上你手动配置的OwnerId,最终出现三个外键。

修复方案

修改OnModelCreating,明确派生类的Ownerships导航属性与Ownership.Owner的关联:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Person>().ToTable("Person");
    modelBuilder.Entity<NaturalPerson>().ToTable("NaturalPerson");
    modelBuilder.Entity<LegalPerson>().ToTable("LegalPerson");
    modelBuilder.Entity<CompanyThatCannotOwnStuff>().ToTable("CompanyThatCannotOwnStuff");
    modelBuilder.Entity<Company>().ToTable("Company");
    modelBuilder.Entity<SharableGood>().ToTable("Good");

    // 配置NaturalPerson与Ownership的关联
    modelBuilder.Entity<NaturalPerson>()
        .HasMany(n => n.Ownerships)
        .WithOne(o => o.Owner)
        .HasForeignKey(o => o.OwnerId)
        .OnDelete(DeleteBehavior.NoAction);

    // 配置Company与Ownership的关联
    modelBuilder.Entity<Company>()
        .HasMany(c => c.Ownerships)
        .WithOne(o => o.Owner)
        .HasForeignKey(o => o.OwnerId)
        .OnDelete(DeleteBehavior.NoAction);

    // 保留SharableGood的关联配置
    modelBuilder.Entity<Ownership>()
        .HasOne(o => o.SharableGood)
        .WithMany()
        .HasForeignKey(o => o.SharableGoodId)
        .OnDelete(DeleteBehavior.NoAction);
}

这样配置后,EF Core会将所有派生类的Ownerships导航属性映射到同一个OwnerId外键,不会再生成多余的CompanyId和NaturalPersonId列。


内容的提问来源于stack exchange,提问作者Seb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 07:45:01