使用 Entity Framework 进行自引用

7

这段代码代表了我问题的小规模:

public class Category
{
    public Guid CategoryID { get; set; }
    public string Name { get; set; }
    public Guid? ParentID { get; set; }
    public bool IsTop { get; set; }
    public string Description { get; set; }

    public virtual Category parentCategory { get; set; }
}

当我在使用Entity Framework中的这个类时,它只会生成一个父子类别关系。我该如何告诉其语义上区分这些属性,并在SQL Server中生成两个不同的关系,一个用于获取所有子类别(递归自上而下的子级关系),另一个用于获取所有父类别(递归自下而上的父级关系)?像这样:
public virtual ICollection<Category> childCategories { get; set;} 
public virtual ICollection<Category> parentCategories { get; set;}

我尝试使用modelBuilder,但是只能获取一层详细信息。


你的每个类别是否有多个父级,还是只有一个?我也不太明白你想要什么。你想要一个包含所有根节点到叶子节点的类别的集合 parentCategories 吗?如果是的话,这不是导航属性,而是遍历的结果或评估的一种形式。EF 无法帮助你,你需要编写自己的代码来创建这样的集合。 - Slauma
是的,我想要像那样的东西,它可以给我树形结构下所有子类别以及同样的方式上到树形结构的所有父类别...我意识到 EF 在这种情况下无法帮助我,所以我已经使用了一些 IEnmuerable 的递归循环,并且它对我有用...感谢您的支持。 - Milan Mendpara
1个回答

7
我在我的一个项目中遇到了检索所有子节点到深度n的问题,这是我的模型中Employee表上经典的主管/员工自引用关系。正如Slauma和Milracle指出的那样,EF不能帮助您检索指定父级下深度为n的所有节点。然而,我能够使用广度优先搜索算法解决这个问题。请注意,我的目标不仅是检索所有子节点,而且要快速地执行此操作,因为使用递归LINQ查询对于管理层的顶层需要超过两分钟。使用这种方法,现在可以在不到两秒钟内执行。
public IEnumerable<string> GetAllSubordinateEmployeeIdsByUserId(string userId)
{
    // Retrieve only the fields that create the self-referencing relationship from all nodes
    var employees = (from e in GetAllEmployees()
                     select new { e.Id, e.SupervisorId });
    // Dictionary with optimal size for searching
    Dictionary<string, string> dicEmployees = new Dictionary<string, string>(employees.Count() * 4);
    // This queue holds any subordinate employees we find so that we may eventually identify their subordinates as well
    Queue<string> subordinates = new Queue<string>();
    // This list holds the child nodes we're searching for
    List<string> subordinateIds = new List<string>();

    // Load the dictionary with all nodes
    foreach (var e in employees)
    {
        dicEmployees.Add(e.Id, e.SupervisorId);
    }

    // Get the key (employee's ID) for each value (employee's supervisor's ID) that matches the value we passed in
    var directReports = (from d in dicEmployees
                         where d.Value == userId
                         select d.Key);

    // Add the child nodes to the queue
    foreach (var d in directReports)
    {
        subordinates.Enqueue(d);
    }

    // While the queue has a node in it...
    while (subordinates.Count > 0)
    {
        // Retrieve the children of the next node in the queue
        var node = subordinates.Dequeue();
        var childNodes = (from e in dicEmployees
                          where e.Value == node
                          select e.Key);
        if (childNodes.Count() != 0)
        {
            // Add the child nodes to the queue
            foreach (var c in childNodes)
            {
                subordinates.Enqueue(c);
            }
        }
        // Add the node from the queue to the list of child nodes
        subordinateIds.Add(node);
    }

    return subordinateIds.AsEnumerable();
}

此外,作为一个注脚,我能够在这篇词典优化文章的帮助下提高字典查找的效率。


这个解决方案难道不是只进行了两级搜索吗?我有什么遗漏吗? - emre nevayeshirazi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接