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

Posted

技术标签:

【中文标题】通过联结表进行多对多自连接【英文标题】:Many to many self join through junction table 【发布时间】:2014-08-20 19:41:44 【问题描述】:

我有一个 EF 模型,它可以通过中间类进行自我引用来定义父/子关系。我知道如何使用Map 命令建立纯粹的多对多关系,但由于某种原因,通过这个中间类导致我的映射出现问题。中间类为关系提供了额外的属性。请参阅下面的类、modelBinder 逻辑和错误:

public class Equipment
        
    [Key]
    public int EquipmentId  get; set; 

    public virtual List<ChildRecord> Parents  get; set; 
    public virtual List<ChildRecord> Children  get; set; 


public class ChildRecord

    [Key]
    public int ChildId  get; set; 

    [Required]
    public int Quantity  get; set; 

    [Required]
    public Equipment Parent  get; set; 

    [Required]
    public Equipment Child  get; set; 

我已经尝试在两个方向上构建映射,但我一次只保留一个集合:

        modelBuilder.Entity<ChildRecord>()
            .HasRequired(x => x.Parent)
            .WithMany(x => x.Children )
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<ChildRecord>()
            .HasRequired(x => x.Child)
            .WithMany(x => x.Parents)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Equipment>()
            .HasMany(x => x.Parents)
            .WithRequired(x => x.Child)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Equipment>()
            .HasMany(x => x.Children)
            .WithRequired(x => x.Parent)
            .WillCascadeOnDelete(false);

无论我使用哪个集合,当我尝试将我的 ef 模型部署到数据库时,我都会收到错误:The foreign key component 'Child' is not a declared property on type 'ChildRecord'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property.

如果我在没有适当的 modelBinder 逻辑的情况下构建它,那么我会在 ChildRecord 表中获得 Child 的两个 ID 列和 Parent 的两个 ID 列。这是有道理的,因为它尝试从 Equipment 自动创建导航属性,并且不知道 ChildRecord 中已有属性可以满足此需求。

我尝试在类上使用数据注释,但没有模型构建器代码,失败并出现与上述相同的错误:

    [Required]
    [ForeignKey("EquipmentId")]
    public Equipment Parent  get; set; 

    [Required]
    [ForeignKey("EquipmentId")]
    public Equipment Child  get; set; 

    [InverseProperty("Child")]
    public virtual List<ChildRecord> Parents  get; set; 
    [InverseProperty("Parent")]
    public virtual List<ChildRecord> Children  get; set; 

我查看了互联网/SO 周围的各种其他答案,共同的区别似乎是我是自我加入的,因为我能找到的所有答案都是针对两种不同类型的。

Entity Framework Code First Many to Many Setup For Existing Tables Many to many relationship with junction table in Entity Framework? Creating many to many junction table in Entity Framework

【问题讨论】:

我尝试了第二个映射 (modelBuilder.Entity&lt;Equipment&gt;()...)。没问题。 (EF 6.1.1,VS 2012)。 @GertArnold 从 EF 6.0.2 升级到 6.1.1 后,如您所建议的,使用第二组时错误消失了;但是输出 SQL 表仍然有四个 ID 列,两个用于子级,两个用于父级。看起来它没有正确地看到这种关系。 这很奇怪。我刚刚从头开始生成了一个数据库,而我的 ChildRecords 表只有两个外键。 @GertArnold 从头开始​​......当你记得重建时会有所帮助......现在它可以工作了。我确实认为这是 EF 版本#。如果您可以发布答案,我很乐意接受。 【参考方案1】:

如果你定义了这个类:

public class Equipment

    [Key]
    public int EquipmentId  get; set; 

    public virtual List<EquipmentRelation> Parents  get; set; 
    public virtual List<EquipmentRelation> Children  get; set; 


public class EquipmentRelation

    [Key]
    [Column("ChildId", Order=1)]
    public int ChildId  get; set; 

    [Key]
    [Column("ParentId", Order=2)]
    public int ParentId  get; set; 

    [Required]
    public int Quantity  get; set; 

    [Required]
    public Equipment Parent  get; set; 

    [Required]
    public Equipment Child  get; set; 

Code First 将正确推断设备部件与其父级和子级之间的关系。父母和孩子都是可选的。 EquipmentRelation 需要父母和孩子。

与您的原始代码的不同之处在于关联表包含一个组合键,其中包含父设备和子设备的 PK。 (我已将其重命名为 EquipmentRelation)。

如果你想对其进行微调,或者“编码文档”,当然可以使用 Fluent API 来表达推断的关系。

注意:Column 属性是必需的,因为当您使用属性定义复合键时,EF 需要知道键中列的顺序。或者,您可以使用 Fluent API HasKey

定义此密钥

【讨论】:

我明白你在说什么,但我不明白它与我在代码中显示的内容有何不同。我有两个一对多的关系,一个导航属性返回到父对象和子对象,以及附加列。我将它构建为一个实体,它可以与附加属性一起访问。您能否根据我在问题中包含的代码澄清您的答案以指出我们的方法之间的差异? 哎呀,对不起,我看错了你的代码。不过,明天我会尽力为您提供解决方案。我现在没时间。 我已经为您的问题添加了一个可行的解决方案,并提供了一些解释。

以上是关于通过联结表进行多对多自连接的主要内容,如果未能解决你的问题,请参考以下文章

多对多自引用表

多个多对多关系(循环关系)

Python学习---django多对多自定义第三方表180206

MS SQL 使用联结表创建多对多关系

多对多关系的联结表是不是应该具有代理主键?

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