多对多自引用关系

Posted

技术标签:

【中文标题】多对多自引用关系【英文标题】:Many-to-many self referencing relationship 【发布时间】:2018-08-19 06:20:23 【问题描述】:

我是 EF 的新手。我遇到了创建的问题。 我尝试使用以下解决方案:Entity Framework Core: many-to-many relationship with same entity

我的实体:

public class WordEntity

    public long Id  get; set; 
    public string Name  get; set; 
    public string Json  get; set; 

    public virtual List<WordSinonymEntity> Sinonyms  get; set; 



public class WordSinonymEntity

    public long WordId  get; set; 
    public virtual WordEntity Word  get; set; 

    public long SinonymId  get; set; 
    public virtual WordEntity Sinonym  get; set; 

和下一个配置:

 modelBuilder.Entity<WordSinonymEntity>()
     .HasOne(pt => pt.Sinonym)
     .WithMany(p => p.Sinonyms)
     .HasForeignKey(pt => pt.SinonymId);

modelBuilder.Entity<WordSinonymEntity>()
    .HasOne(pt => pt.Word)
    .WithMany(t => t.Sinonyms)
    .HasForeignKey(pt => pt.WordId);`

但它会导致下一个异常。

System.InvalidOperationException: '无法在 'WordEntity.Sinonyms' 和 'WordSinonymEntity.Word' 之间创建关系,因为 'WordEntity.Sinonyms' 和 'WordSinonymEntity.Sinonym' 之间已经存在关系。导航属性只能参与单个关系。'

有没有人可以帮助我或者可以建议一些例子来学习? 谢谢。

【问题讨论】:

【参考方案1】:

您关注的帖子肯定是错误的。

每个集合或引用导航属性只能成为单个关系的一部分。虽然与显式连接实体的多对多关系是通过 two 一对多关系实现的。连接实体包含两个引用导航属性,但主实体只有单个集合导航属性,它必须与其中一个关联,但不能同时与两者关联。

解决此问题的一种方法是添加第二个集合导航属性:

public class WordEntity

    public long Id  get; set; 
    public string Name  get; set; 
    public string Json  get; set; 

    public virtual List<WordSinonymEntity> Sinonyms  get; set; 
    public virtual List<WordSinonymEntity> SinonymOf  get; set;  // <--

并通过 fluent API 指定关联:

modelBuilder.Entity<WordSinonymEntity>()
     .HasOne(pt => pt.Sinonym)
     .WithMany(p => p.SinonymOf) // <--
     .HasForeignKey(pt => pt.SinonymId)
     .OnDelete(DeleteBehavior.Restrict); // see the note at the end

modelBuilder.Entity<WordSinonymEntity>()
    .HasOne(pt => pt.Word)
    .WithMany(t => t.Sinonyms)
    .HasForeignKey(pt => pt.WordId); 

另一种方法是保持模型不变,但将WordSinonymEntity.Sinonym 映射到单向 关联(具有引用导航属性且没有相应的集合导航属性):

modelBuilder.Entity<WordSinonymEntity>()
     .HasOne(pt => pt.Sinonym)
     .WithMany() // <--
     .HasForeignKey(pt => pt.SinonymId)
     .OnDelete(DeleteBehavior.Restrict); // see the note at the end

modelBuilder.Entity<WordSinonymEntity>()
    .HasOne(pt => pt.Word)
    .WithMany(t => t.Sinonyms)
    .HasForeignKey(pt => pt.WordId); 

只需确保WithMany 与相应导航属性的存在/不存在完全匹配即可。

请注意,在这两种情况下,您都必须为至少一个关系关闭删除级联,并在删除主实体之前手动删除相关的连接实体,因为自引用关系总是会引入可能的循环或多个级联路径 问题,阻止使用级联删除。

【讨论】:

非常感谢。我已经尝试了两种解决方案的变体,并且两种变体都很完美。我浪费了很多时间来寻找答案。我不得不在 *** 上早点提出这个问题。 嘿,Ivan 先生,如果我真的需要级联怎么办,我有类似的方法,我需要onUpdate: ReferantialAction.Cascade,您能告诉我吗? :) @Burak 呵呵,当然。但是......不幸的是,EF Core 不支持更新(修改)密钥,因此没有流畅的配置。您必须手动修改生成的迁移,然后通过原始 SQL 命令执行更新,因为如开头所述,EF Core 不会让您通过上下文 API 更改实体 PK。 你能检查一下这个问题吗...***.com/questions/63472076/… 很晚才提出其他问题,但我正在解决类似的问题。在此示例中,如何从 WordEntity 返回它所属的所有同义词的列表? IE。 Synonyms 和 SynonymsOf 的串联列表。我的第一个想法是简单地添加另一个带有 getter 的属性,该 getter 完全返回 (Synonyms.Concat(SynonymsOf)),但是有 EF Core 方法吗?

以上是关于多对多自引用关系的主要内容,如果未能解决你的问题,请参考以下文章

多对多自引用表

带有额外列的多对多自引用原则

通过联结表进行多对多自连接

Sequelize 多对多自引用

从教义中的多对多自引用实体中获取孩子

如何使用 Doctrine 2 创建与额外字段的多对多自引用关联?