这就是我认为正在发生的事情。当您只有 ParentIncident Code First 时,按照惯例将此映射为自引用一对多单向独立关联的一端。那里有很多行话——让我解释一下:
- 自引用:从问题中可以看出,Indicent 实体位于关系的两端。
- 一对多:每个 Indent 都有一个 ParentIncident,并且一个 Incident 可以是许多 Incidents 的父项。
- 单向:有从 Incident 到其父事件 (ParentIncident) 的导航属性,但没有子事件的反向集合导航属性。
- 独立关联:由于类中没有外键属性,EF 在数据库中创建了一个 FK 列(称为 ParentIncident_IncidentId),但它没有映射到任何对象属性。
现在,当您添加 ClaimIncident 属性时,这会更改 Code First 尝试映射的方式。它现在创建了一个自引用的一对一双向 FK 关联:
- 一对一,因为它看到从事件到事件的两个导航属性,并假设它们是同一关系的两个方面。由于两者都不是集合属性,因此关系是一对一的。
- 再次双向,因为现在关系的每一端都有一个导航属性。
- FK 因为一对一的关系变成了 PK 到 PK 并且 EF 总是将其视为 FK 关系,因为 FK(这是一个 PK)被映射。
但是 Code First 无法弄清楚这种一对一关系的哪一边应该是主端,哪边应该是从属端。有时这确实很重要......所以代码优先抛出。
好的,所以我将从线程和您的模型推断出您在这里并不真正想要一对一的关系。(一对一的自引用 PK 到 PK 关系是没有意义的,所以你可以争辩说 Code First 不应该创建它,但 Code First 并不那么聪明。)
所以你把FKs放进去。发生了两件事。首先,Code First 现在假定这些必须再次是一对多关系。其次,Code First 现在有了 FK 属性的名称,然后使用该名称来命名数据库中的 FK 列。但该名称与已经为独立关联创建的 FK 列不同——它不是 ParentIncident_IncidentId。这意味着迁移将不得不删除此列并创建一个新列...导致数据丢失。
要告诉 Code First 你真的只需要两个自引用、单向、一对多、独立的关联,请使用以下流畅的映射:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Incident>()
.HasOptional(e => e.ParentIncident)
.WithMany();
modelBuilder.Entity<Incident>()
.HasOptional(e => e.ClaimIncident)
.WithMany();
}
现在迁移应该正确更新您的数据库。现在,必须说您可能要考虑更改为 FK 关联,在这种情况下,您要么需要在 Migrations 中进行一些数据移动,如果有数据丢失就接受,或者告诉 Code First 继续使用 FK它之前生成的列名。