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!;
}
以及以下配置EntityTypeConfiguration
s:
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)
//...
问题是 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!;
}
以及以下配置EntityTypeConfiguration
s:
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)
//...