EntityFramework Core 3.0 Fluent API 多对多关系构建器创建额外的外键列

EntityFramework Core 3.0 Fluent API Many-to-many relationship builder creating extra foreign key columns

问题是 EF 核心正在为多对多关系创建额外的外键列,即使我已经指定了外键是什么。

我有以下实体模型,需要多对多关系

ParticipantModel.cs

public class ParticipantModel
{
    public int Id { get; set; }

    public HashSet<ThreadsParticipants> ThreadsParticipants { get; set; } = new HashSet<ThreadsParticipants>();
}

ThreadModel.cs

public class ThreadModel
{
    public int Id { get; private set; }

    public HashSet<ThreadsParticipants> ThreadsParticipants { get; set; } = new HashSet<ThreadsParticipants>();
}

ThreadsParticipants.cs(加入table)

public class ThreadsParticipants
{
    public int ThreadModelId { get; private set; }

    public ThreadModel ThreadModel { get; private set; } = default!;

    public int ParticipantModelId { get; private set; }

    public ParticipantModel ParticipantModel { get; private set; } = default!;
}

以及以下配置EntityTypeConfigurations:

    public void Configure(EntityTypeBuilder<ParticipantModel> builder)
    {
        builder.HasKey(p => p.Id);
    }

    public void Configure(EntityTypeBuilder<ThreadModel> builder)
    {
        builder.HasKey(t => t.Id);
    }

    public void Configure(EntityTypeBuilder<ThreadsParticipants> builder)
    {
        builder
            .HasKey(tp => new { tp.ThreadModelId, tp.ParticipantModelId });

        builder
            .HasOne(tp => tp.ThreadModel)
            .WithMany()
            .HasForeignKey(tp => tp.ThreadModelId)
            .IsRequired();

        builder
            .HasOne(tp => tp.ParticipantModel)
            .WithMany()
            .HasForeignKey(tp => tp.ParticipantModelId)
            .IsRequired();
    }

当 运行 add-migration 创建 ThreadsParticipants table 的块是这样生成的:

            migrationBuilder.CreateTable(
            name: "MessageThreadsParticipants",
            columns: table => new
            {
                ThreadModelId = table.Column<int>(nullable: false),
                ParticipantModelId = table.Column<int>(nullable: false),
                ParticipantModelId1 = table.Column<int>(nullable: true),
                ThreadModelId1 = table.Column<int>(nullable: true)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_MessageThreadsParticipants", x => new { x.ThreadModelId, x.ParticipantModelId });
                table.ForeignKey(
                    name: "FK_MessageThreadsParticipants_MessageParticipants_ParticipantModelId",
                    column: x => x.ParticipantModelId,
                    principalTable: "MessageParticipants",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);
                table.ForeignKey(
                    name: "FK_MessageThreadsParticipants_MessageParticipants_ParticipantModelId1",
                    column: x => x.ParticipantModelId1,
                    principalTable: "MessageParticipants",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Restrict);
                table.ForeignKey(
                    name: "FK_MessageThreadsParticipants_MessageThreads_ThreadModelId",
                    column: x => x.ThreadModelId,
                    principalTable: "MessageThreads",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);
                table.ForeignKey(
                    name: "FK_MessageThreadsParticipants_MessageThreads_ThreadModelId1",
                    column: x => x.ThreadModelId1,
                    principalTable: "MessageThreads",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Restrict);
            });

当我明确指定外键是什么时,为什么会生成这些额外的外键属性?

编辑

根据@Flater 的评论,原因是配置被指定了两次,因此创建了一组额外的列。


实际上从 ThreadsParticipants 实体类型配置中删除双工 one-to-many Fluent API 配置解决了这个问题,不再创建额外的属性。当这部分配置被删除时 EF 使用命名约定来确定关系,因此无需明确说明关系详细信息。

    builder
        .HasOne(tp => tp.ThreadModel)
        .WithMany()
        .HasForeignKey(tp => tp.ThreadModelId)
        .IsRequired();

    builder
        .HasOne(tp => tp.ParticipantModel)
        .WithMany()
        .HasForeignKey(tp => tp.ParticipantModelId)
        .IsRequired();

这让我认为 属性 名称不匹配导致了所有问题。

事情是这样的,你有一组外键,它们也作为那个 table 的组合键。 Entity framework 必须想办法解决一些命名纠纷。您可以向模型显式添加数据注释以抑制此问题。

[ForeignKey("ThreadModel")]
public int ThreadModelId { get; private set; }

还有另一个属性
为什么不通过添加

来说明你们的完整关系
//...
.WithMany(x => x.ThreadsParticipants)
//...