实体框架中的第二个 Self-To-Self 关系

发布于 2024-12-28 05:57:50 字数 600 浏览 6 评论 0原文

假设我们有一个域类,

public class Incident
{
    [Key]
    public virtual int IncidentId { get; set; }

    [Display(Name = "Parent Incident")]
    public virtual Incident ParentIncident { get; set; }

    [Display(Name = "Related Claim")]
    public virtual Incident ClaimIncident { get; set; }

 }

为了简单起见,省略了其他属性。

当我刚刚完成ParentIncident时,一切都工作正常。现在我已将 ClaimIncident 添加到类中。我尝试使用 Entity Framework 4.3 (PreRelease) Migrations 选项更新我的数据库,但收到错误消息,EF 不知道如何将事件映射到事件。

为什么每个类允许引用同一个类实例一次,而当我引入第二个类实例时,它突然不知道如何引用它?我怎样才能纠正模型类?

Assume we have a domain class

public class Incident
{
    [Key]
    public virtual int IncidentId { get; set; }

    [Display(Name = "Parent Incident")]
    public virtual Incident ParentIncident { get; set; }

    [Display(Name = "Related Claim")]
    public virtual Incident ClaimIncident { get; set; }

 }

Other properties are omitted for simplicity.

When I had just ParentIncident in place, everything worked fine. Now I have added ClaimIncident to the class. I am attempting to update my database using the Entity Framework 4.3 (PreRelease) Migrations option, but I am getting an error, that EF doesn't know how to map Incident to Incident.

Why referencing the same class instance once is allowed per class, and when I introduce the second one, it suddenly has no clue of how to reference it? And how can I correct the model class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情话已封尘 2025-01-04 05:57:50

这就是我认为正在发生的事情。当您只有 ParentIncident Code First 时,按照惯例将其映射为自引用一对多单向独立关联的一端。这里有很多行话——让我解释一下:

  • 自引用:从问题中可以看出,Indicent 实体位于关系的两端。
  • 一对多:每个事件都有一个父事件,一个事件可以是许多事件的父事件。
  • 单向:有从事件到其父事件 (ParentIncident) 的导航属性,但没有子事件的反向集合导航属性。
  • 独立关联:由于类中没有外键属性,EF 在数据库中创建一个 FK 列(称为 ParentIncident_IncidentId),但它未映射到任何对象属性。

现在,当您添加 ClaimIncident 属性时,这会更改 Code First 尝试映射的方式。它现在创建一个自引用的一对一双向 FK 关联:

  • 一对一,因为它看到从事件到事件的两个导航属性,并假设它们是同一关系的两侧。由于两者都不是集合属性,因此关系是一对一的。
  • 再次是双向的,因为现在关系的每一端都有一个导航属性。
  • FK,因为一对一关系变为 PK 到 PK,并且 EF 始终将其视为 FK 关系,因为 FK(即 PK)已映射。

但 Code First 无法弄清楚这种一对一关系的哪一端应该是主体端,哪一端应该是从属端。有时这确实很重要......所以 Code First 会抛出异常。

好的,所以我将从线程和您的模型中推断出您实际上并不想要这里存在一对一的关系。 (一对一的自引用 PK 到 PK 关系是毫无意义的,因此您可能会认为 Code First 不应该创建它,但 Code First 并不那么聪明。)

因此您将 FK 放入其中。两个事情发生了。首先,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之前生成的列名称。

This is what I think is happening. When you only had ParentIncident Code First was mapping this by convention as one end of a self-referencing one-to-many unidirectional independent association. Lots of jargon there--let me explain:

  • Self-referencing: as is apprarant from the question, the Indicent entity is at both ends of the relationship.
  • One-to-many: Each Indicent has a single ParentIncident and an Incident can be the parent for many Incidents.
  • Unidirectional: There is a navigation property from the Incident to its parent (ParentIncident) but no reverse collection navigation properties of child incidents.
  • Indpendent Association: Since there is no foreign key property in the class, EF creates an FK column in the database (called ParentIncident_IncidentId) but it isn't mapped to any object property.

Now, when you add the ClaimIncident property this changes the way Code First tries to map. It now creates a self-referencing one-to-one bidirectional FK association:

  • One-to-one because it sees two navigation properties from Incident to Incident and assumes that these are two sides of the same relationship. Since neither is a collection property, the relationship is one-to-one.
  • Bidirectional again because there is now a nvaigation property on each end of the relationship.
  • FK because the one-to-one relationship becomes PK to PK and EF always treats this as an FK relationship since the FK (which is a PK) is mapped.

But Code First can't figure out which side of this one-to-one relationship should be the principal end, and which should be the dependant end. And this does matter sometimes...so Code First throws.

Okay, so I'm going to infer from the thread and your model that you don't really want a one-to-one relationship here. (A one-to-one self-referencing PK to PK relationship is kind of pointless, so you could argue that Code First shouldn't create it, but Code First isn't that smart.)

So you put the FKs in. Two things happen. First, Code First now assumes that these must be one-to-many relationships again. Second, Code First now has a name for the FK property and in turn uses that name to name the FK column in the database. But that name is not the same as the FK column already created for the independent associated--it's not ParentIncident_IncidentId. This means that Migrations would have to drop this column and create a new one...resulting in data loss.

To tell Code First that you really just want two self-referencing, unidirectional, one-to-many, independent associations, use this fluent mapping:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Incident>()
        .HasOptional(e => e.ParentIncident)
        .WithMany();

    modelBuilder.Entity<Incident>()
        .HasOptional(e => e.ClaimIncident)
        .WithMany();
}

Now Migrations should correctly update your database. Now, it must be said that you may want to consider changing to FK associations, in which case you either need to do some data motion in Migrations, just accept the data loss if there is any, or tell Code First to keep using the FK column name that it previously generated.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文