代码之家  ›  专栏  ›  技术社区  ›  djdd87

有没有办法,使用LINQ/EF,获得父/子层次结构中最顶层的项?

  •  4
  • djdd87  · 技术社区  · 15 年前

    我有一个班叫 Structure :

    public class Structure
    {
        public int StructureId { get; set; }
        public Structure Parent { get; set; }
    }
    

    如你所见, 结构 有父母 结构 . 在这个层次结构中可以有不定数量的结构。

    有没有办法,使用LINQ(带有实体框架)来获得 最上面的 这个层次结构?

    目前,为了找到最顶尖的家长,我不得不多次访问数据库。最上层的家长是 结构 空值 Parent 属性:

    Structure structure = structureRepository.Get(id);
    while (structure.Parent != null)
    {
        structure = structureRepository.Get(structure.Parent.StructureId);
    }
    
    // When we're here; `structure` is now the top most parent.
    

    那么,使用LINQ/Lambdas有什么优雅的方法可以做到这一点吗?理想情况下,从以下代码开始:

    var structureQuery = from item in context.Structures
                         where item.StructureId == structureId
                         select item;
    

    我只想写一些类似于下面的东西,这样我只触发一个数据库命中:

    structureQuery = Magic(structureQuery);
    Structure topMostParent = structureQuery.Single();
    
    5 回复  |  直到 15 年前
        1
  •  2
  •   Chris Pitman    15 年前

    这不是一个直接的答案,但是你遇到的问题与你存储树的方式有关。有两种方法可以通过不同的数据结构简化此查询。

    一是使用 Nested Set Hierarchy ,它可以简化跨树的多种查询。

    另一种方法是存储祖先/子代/深度元组的表示化表。然后,此查询将查找当前结构为具有最大深度的子体的元组。

        2
  •  2
  •   djdd87    15 年前

    我想最好的办法是从我想要的顶级父级结构中一次性加载整个层次结构:

    var structureQuery = from item in context.Structures
                             .Include(x => x.Parent)
                         where item.StructureId == structureId
                         select item;
    

    然后使用代码:

    while (structure.Parent != null)
    {
        structure = structure.Parent;
    }
    
        3
  •  0
  •   jeroenh    15 年前

    我也有类似的情况。我没能用LINQ/EF直接解决这个问题。相反,我通过使用递归公共表表达式创建数据库视图来解决这个问题,如前所述 here . 我创建了一个用户定义函数,该函数交叉应用所有父对象到一个子对象(反之亦然),然后创建了一个视图,该视图使用了我导入到EF对象上下文中的该用户定义函数。

    (免责声明:简化代码,我并没有实际测试这个)

    我有两个表,比如MyTable(包含所有项)和MyParentChildTable(包含ChildId,ParentId关系)

    然后我定义了以下自定义项:

    CREATE FUNCTION dbo.fn_getsupertree(@childid AS INT) 
        RETURNS @TREE TABLE
    (
         ChildId INT NOT NULL
        ,ParentId  INT NULL
        ,Level   INT NOT NULL
    )
    AS
    BEGIN
      WITH Parent_Tree(ChildId, ParentId)
      AS
      ( 
        -- Anchor Member (AM)
        SELECT ChildId, ParentId, 0
        FROM MyParentChildTable
        WHERE ChildId = @childid
    
        UNION all
    
        -- Recursive Member (RM)
        SELECT info.ChildId, info.ParentId, tree.[Level]+1
        FROM MyParentChildTable AS info
          JOIN Parent_Tree AS tree
            ON info.ChildId = tree.ParentId
      )
      INSERT INTO @TREE
        SELECT * FROM Parent_Tree;
    
      RETURN
    END
    

    以及以下视图:

    CREATE VIEW VwSuperTree AS (
    SELECT tree.*
    FROM MyTable
    CROSS APPLY fn_getsupertree(MyTable.Id) as tree
    )
    GO
    

    这就给了我每个孩子,所有的父母都有他们的“树级”(直接父母有1级,父母的父母有2级,等等)。从这个视图中,可以很容易地查询具有最高级别的项。我刚刚在EF上下文中导入了视图,以便能够用LINQ查询它。

        4
  •  -1
  •   danijels    15 年前

    我喜欢这个问题,想不出一个可行的办法。但您可以在您的存储库类上实现它吗?毕竟,高层应该只有一个,如果有必要的话,也许它应该 structureRepository.GetRoot() 或者别的什么。

        5
  •  -4
  •   Agent_9191    15 年前

    例如,可以使用linq take构造

                var first3Customers = (
                    from c in customers
                    select new {c.CustomerID, c.CustomerName} )
                .Take(2);