Есть ли способ, используя LINQ / EF, получить самый верхний элемент в иерархии родитель / потомок? - PullRequest
4 голосов
/ 06 октября 2010

У меня есть класс с именем Structure:

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

Как видите, у Structure есть родительский элемент Structure.В этой иерархии может быть неограниченное количество структур.

Есть ли способ, используя LINQ (с Entity Framework), получить самую верхнюю структуру в этой иерархии?

В настоящее время мне приходится заходить в базу данных несколько раз, чтобы найти самого лучшего родителя.Самый верхний родительский элемент - это 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 ]

2 голосов
/ 06 октября 2010

Я думаю, что лучшее, что я собираюсь получить, это загрузить всю иерархию одним ударом из структуры, для которой я хочу верхний родительский элемент:

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 голосов
/ 06 октября 2010

Это не прямой ответ, но ваша проблема связана с тем, как вы храните свое дерево. Есть несколько способов упростить этот запрос, структурируя данные по-разному.

Одним из них является использование Иерархия вложенных множеств , которая может упростить многие виды запросов для деревьев.

Другой способ - хранить деномрализованную таблицу кортежей Ancestor / Descendant / Depth. Этот запрос затем находит кортеж с текущей структурой в качестве потомка с максимальной глубиной.

0 голосов
/ 06 октября 2010

вы можете использовать конструкцию linq take, например

            var first3Customers = (
                from c in customers
                select new {c.CustomerID, c.CustomerName} )
            .Take(2);
0 голосов
/ 06 октября 2010

У меня похожая ситуация. Мне не удалось решить это напрямую с помощью LINQ / EF. Вместо этого я решил создать представление базы данных с использованием рекурсивных общих табличных выражений, как указано здесь . Я сделал пользовательскую функцию, которая перекрестно применяет всех родителей к дочернему элементу (или наоборот), затем представление, которое использует эту пользовательскую функцию, которую я импортировал в контекст моего объекта EF.

(отказ от ответственности: упрощенный код, я на самом деле не проверял это)

У меня есть две таблицы: MyTable (содержащая все элементы) и MyParentChildTable, содержащие отношение ChildId, ParentId

Затем я определил следующий 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.

0 голосов
/ 06 октября 2010

Мне нравится вопрос, и я не могу придумать, как это сделать.Но не могли бы вы реализовать это в своем классе репозитория?В конце концов, на вершине должен быть только один, и если в этом есть необходимость, то, возможно, он заслуживает structureRepository.GetRoot() или чего-то еще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...