Имитация рекурсии CTE в C # - PullRequest
10 голосов
/ 03 июня 2011

Скажите, что у вас есть следующий CTE, который возвращает уровень некоторых данных дерева (модель смежности), которые у меня есть (взят из Иерархические данные в Linq - параметры и производительность ):

WITH hierarchy_cte(id, parent_id, data, lvl) AS
(
    SELECT id, parent_id, data, 0 AS lvl
    FROM dbo.hierarchical_table
    WHERE (parent_id IS NULL)

    UNION ALL

    SELECT t1.id, t1.parent_id, t1.data, h.lvl + 1 AS lvl
    FROM dbo.hierarchical_table AS t1 
    INNER JOIN hierarchy_cte AS h ON t1.parent_id = h.id
)
SELECT id, parent_id, data, lvl
FROM hierarchy_cte AS result

Мне было интересно, будет ли увеличение производительности при выполнении рекурсии в C # вместо SQL. Может кто-нибудь показать мне, как выполнять ту же работу, что и CTE, с рекурсивной функцией C #, если у меня есть IQueryable, где Tree - это объект, представляющий запись в иерархической таблице? Что-то вроде:

public void RecurseTree(IQueryable<Tree> tree, Guid userId, Guid parentId, int level)
{
    ...
    currentNode.level = x
    ...
    Recurse(tree... ,level + 1)
}

Было бы здорово увидеть, что это легко сделать с помощью лямбда-выражения.

Ответы [ 2 ]

5 голосов
/ 03 июня 2011

Рекурсия в SQL Server ужасно медленная по сравнению, но она работает.

Я бы сказал, что T-SQL несколько ограничен, но он никогда не предназначался для выполнения всех этих операций. Я не верю, что есть какой-то способ сделать это с помощью IQueryable, если вы решили запустить его на своем экземпляре SQL Server, но вы можете сделать это в памяти на машине, на которой выполняется код, используя LINQ-to-Objects относительно компактная манера.

Вот один из способов сделать это:

class TreeNode
{
    public int Id;
    public int? ParentId;
}

static void Main(string[] args)
{
    var list = new List<TreeNode>{
        new TreeNode{ Id = 1 },
            new TreeNode{ Id = 4, ParentId = 1 },
            new TreeNode{ Id = 5, ParentId = 1 },
            new TreeNode{ Id = 6, ParentId = 1 },
        new TreeNode{ Id = 2 },
            new TreeNode{ Id = 7, ParentId= 2 },
                new TreeNode{ Id = 8, ParentId= 7 },
        new TreeNode{ Id = 3 },
    };

    foreach (var item in Level(list, null, 0))
    {
        Console.WriteLine("Id={0}, Level={1}", item.Key, item.Value);
    }
}

private static IEnumerable<KeyValuePair<int,int>> Level(List<TreeNode> list, int? parentId, int lvl)
{
    return list
        .Where(x => x.ParentId == parentId)
        .SelectMany(x => 
            new[] { new KeyValuePair<int, int>(x.Id, lvl) }.Concat(Level(list, x.Id, lvl + 1))
        );
}
5 голосов
/ 03 июня 2011

Исключительно рекурсивные лямбды (и, как следствие, Expression с) технически возможны, но в значительной степени безумны .Я также ожидал бы, что любой синтаксический анализатор (L2S, EF и т. Д.), За исключением возможно LINQ-to-Objects, просто сойдет с ума, пытаясь отменить это.это неподдерживаемый механизм для Expression.

Наконец, обратите внимание, что только то, что вы пишете Expression, не означает, что вы выполняете его в C # - фактически, скорее всего, наоборот: если выактивно пишу Expression (а не делегатский или процедурный код). Я предполагаю, что он собирается анализатором (если вы не использовали .AsQueryable() для передачи его в LINQ-to-Objects).

...