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

本文关键字:自连接 连接 | 更新日期: 2023-09-27 18:18:57

我有一个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列。这是有意义的,因为它试图从设备自动创建导航属性,而不知道在ChildRecord中已经有属性来满足这个需求。

我尝试在类上使用数据注释,没有modelBuilder代码,这失败了与上面相同的错误:

    [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,共同的区别似乎是我自己加入,因为我能找到的所有答案都是针对两种不同的类型。

  • 实体框架代码先多对多设置现有表
  • 实体框架中连接表的多对多关系?
  • 在实体框架中创建多对多连接表

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

如果你定义了这个类:

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将正确地推断出设备部件与其父级和子级之间的关系。父节点和子节点都是可选的。设备关系要求父级和子级。

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

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

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

定义此键。