【问题标题】:EntityFramework Core 3.0 Fluent API Many-to-many relationship builder creating extra foreign key columnsEntityFramework Core 3.0 Fluent API 多对多关系构建器创建额外的外键列
【发布时间】:2020-08-04 11:08:04
【问题描述】:

问题在于 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(加入表)

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 表的块:

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

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

【问题讨论】:

  • 只是为了帮助您沟通问题:如果您遵循 EF 文档标准,这不是多对多关系。这是两个单独的一对多关系(您在功能上将其用作多对多,但 EF 并不关心您的意图)。在 EF 的上下文中,多对多是指您不指定连接表的情况,EF 为您制作并隐藏它。请注意,EF Core 目前不支持多对多(原来的 EF 确实支持)。
  • 感谢您的澄清

标签: c# .net-core entity-framework-core ef-fluent-api


【解决方案1】:

编辑

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


实际上从ThreadsParticipants实体类型配置中删除双工一对多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();

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

【讨论】:

  • 我怀疑这不是不匹配,因为它是两次附加模型构建器配置的问题(按照约定和明确)。但无论如何,很高兴看到您找到了解决方案。
【解决方案2】:

事情就是这样,您有一组外键,它们也用作该表的复合键。实体框架必须想办法解决一些命名争议。您可以向模型显式添加数据注释以抑制此问题。

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

还有其他属性
为什么不通过添加来说明您的完整关系

//...
.WithMany(x => x.ThreadsParticipants)
//...

【讨论】:

  • 根据EF的约定标准,当FK属性名称为name of nav prop + "Id"时,EF会自动假设这两个是链接的。您的代码没有错,但按照 EF 约定,它是多余的。
猜你喜欢
  • 2020-01-18
  • 1970-01-01
  • 2020-11-28
  • 1970-01-01
  • 2015-06-08
  • 2014-03-25
  • 2020-05-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多