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

时间:2010-10-06 15:16:56

标签: linq c#-4.0 entity-framework-4 lambda

我有一个名为Structure的课程:

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

如您所见,Structure有一个父Structure。在这个层次结构中可以有无限数量的结构。

有没有办法,使用LINQ(带有实体框架)来获得此层次结构中的最顶层结构?

目前,为了找到最顶层的父母,我不得不多次访问数据库。最顶层的父级是Structure,其属性为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 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:2)

我认为我要得到的最好的是从我希望得到顶级父母的结构中加载整个层次结构:

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

然后只使用代码:

while (structure.Parent != null)
{
    structure = structure.Parent;
}

答案 2 :(得分:0)

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

(免责声明:简化代码,我实际上没有对此进行测试)

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

然后我定义了以下udf:

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查询它。

答案 3 :(得分:-1)

我喜欢这个问题而不能想到这样做的方法。但是你可以在你的存储库类中实现它吗?毕竟,顶部应该只有一个,如果需要它,那么它可能值得structureRepository.GetRoot()或其他东西。

答案 4 :(得分:-4)

你可以使用linq take构造,例如

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