我有三个类,它们应该将一个概念一对一地分成 3 个表。从字面上看,我想拼凑一张巨大的桌子。
class MainHolder
{
public Guid Id { get; set; }
public FirstPart FirstPart { get; set; }
public SecondPart SecondPart { get; set; }
}
class FirstPart
{
public Guid Id { get; set; }
public string Title { get; set; }
}
class SecondPart
{
public Guid Id { get; set; }
public string Description { get; set; }
}
我正在设置上下文和关系如下。
public DbSet<MainHolder> Holders { get; set; }
public DbSet<FirstPart> FirstParts { get; set; }
public DbSet<SecondPart> SecondParts { get; set; }
protected override void OnModelCreating(ModelBuilder model)
{
model.Entity<FirstPart>()
.HasOne<MainHolder>()
.WithOne(a => a.FirstPart)
.HasForeignKey<MainHolder>();
model.Entity<SecondPart>()
.HasOne<MainHolder>()
.WithOne(a => a.SecondPart)
.HasForeignKey<MainHolder>();
}
当我创建迁移时,主要持有者包含 2 列 - 第一列是其自身的 ID,另一列是第二个实体的 ID 但 不是第一个实体。外键按预期设置:只是不完整的列。
migrationBuilder.CreateTable(name: "Holders", columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
SecondPartId = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Holders", x => x.Id);
table.ForeignKey(
name: "FK_Holders_FirstParts_Id",
column: x => x.Id,
principalTable: "FirstParts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Holders_SecondParts_SecondPartId",
column: x => x.SecondPartId,
principalTable: "SecondParts",
principalColumn: "Id");
});
migrationBuilder.CreateTable(name: "FirstParts", columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Title = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table => { table.PrimaryKey("PK_FirstParts", x => x.Id); });
migrationBuilder.CreateTable(name: "SecondParts", columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Description = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table => { table.PrimaryKey("PK_SecondParts", x => x.Id); });
我注意到 FK 在命名和级联策略方面的设置略有不同。我无法终生解释为什么会发生这种情况,因为两个实体的声明和配置方式完全相同。
我错过了什么?
如果是由于管理多个一对一表的某种约定,那么我很遗憾找不到任何类型的文档。我担心实际上存在偏差,我只是想意识到这一点。帮助我理解。
尝试删除 .HasForegnKey 我认为 EF 可能会对此感到困惑,因为它期望每个关系在 MainHolder 上都有一个唯一的 FK,并为第一种情况使用默认 ID 并为第二种情况生成一个。默认情况下,两个表上的 PK 之间将建立一对一的关系,这看起来是您设置和期望的方式,因此只需为两者定义 HasOne.WithOne 就可以按预期工作。
HasForeignKey 通常用于 1 对 1 关系,在这种关系中,您希望在关系的一侧指定一个单独的外键。例如,如果您想要在 MainHolder 上使用 FirstPartId 而不是在 PK 上加入两者。默认行为是对两个表都使用 PK。我建议的另一个变化是从 MainHolder 的角度而不是从 First 和 Second 的角度定义一对一的关系。
所以代替:
model.Entity<FirstPart>()
.HasOne<MainHolder>()
.WithOne(a => a.FirstPart)
.HasForeignKey<MainHolder>();
model.Entity<SecondPart>()
.HasOne<MainHolder>()
.WithOne(a => a.SecondPart)
.HasForeignKey<MainHolder>();
使用
model.Entity<MainHolder>()
.HasOne(a => a.FirstPart)
.WithOne();
model.Entity<MainHolder>()
.HasOne(a => a.SecondPart)
.WithOne();
关于支持可选双向导航属性的规则在 EF 核心中有些不固定,因此如果 FirstPart 和 SecondPart 上没有“MainHolder”引用,WithOne 可能无法按预期工作。如果 EF Core 抱怨,解决方法通常是添加
protected
(或可能是 private
)引用。一对一关系通常受益于双向引用,其中 MainHolder 包含对 FirstPart 的引用,而 FirstPart 包含返回其 MainHolder 的引用。该导航属性不需要 ForeignKey
属性或在一对一关系的一部分时定义的映射。