正在为同一个表创建多个自引用外键

本文关键字:自引用 创建 同一个 | 更新日期: 2023-09-27 18:22:04

我有一个这样的类:

[Table("Tree")]
public class Tree 
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int TreeId { get; set; }
    public int? TreeOneId { get; set; }
    [ForeignKey("TreeOneId")]
    public virtual Tree TreeOne { get; set; }
    public int? TreeTwoId { get; set; }
    [ForeignKey("TreeTwoId")]
    public virtual Tree TreeTwo { get; set; }
}

当我从这个类定义创建数据库时,我得到了错误:

无法确定类型"树"answers"树"之间关联的主体端。此关联的主体端必须使用关系fluent API或数据注释显式配置。

如果我删除其中一个属性,并创建一个类定义,如下所示:

[Table("Tree")]
public class Tree 
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int TreeId { get; set; }
    public int? TreeOneId { get; set; }
    [ForeignKey("TreeOneId")]
    public virtual Tree TreeOne { get; set; }
    //public int? TreeTwoId { get; set; }
    //[ForeignKey("TreeTwoId")]
    //public virtual Tree TreeTwo { get; set; }
}

它确实创建了正确的表。我怎样才能克服这个错误?我已经尝试指定列顺序,添加一个唯一的索引。我可以通过使用ListTreeOne来解决这个问题,它将创建相应的链接表,但我宁愿不必使用该解决方案。

正在为同一个表创建多个自引用外键

这里的基本问题是,SQL不允许精确定义二进制树。

在您的方法中,一个树项可能有多个父项,因为没有什么可以阻止LeftId和RightId指向同一引用或使另一个树项目指向同一子树。

我们需要为Left/Right上的导航属性定义一个端点。这是解决方案的关键:为左/右子项定义一个单独的反向导航属性,并考虑到有多个父项的可能性。

考虑到这一点,让我们为实体框架建立模型。注意,我用TreeItem替换了Tree,因为它每个树节点只有一个条目,并使用了LeftRight而不是您的属性名称。

public class TreeItem
{
    public int Id { get; set; }
    public int? LeftId { get; set; }
    public int? RightId { get; set; }
    [ForeignKey("LeftId")]
    [InverseProperty("Parent1")]
    public virtual TreeItem Left { get; set; }
    [ForeignKey("RightId")]
    [InverseProperty("Parent2")]
    public virtual TreeItem Right { get; set; }
    [InverseProperty("Left")]
    public virtual ICollection<TreeItem> Parent1 { get; set; }
    [InverseProperty("Right")]
    public virtual ICollection<TreeItem> Parent2 { get; set; }
}

另请注意:我只在EF6中进行了测试,因此不能保证使用EF5