在C#中模拟CTE递归

wog*_*les 10 c# sql recursion common-table-expression entity-framework-4

假设有以下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
Run Code Online (Sandbox Code Playgroud)

我想知道通过在C#而不是SQL中进行递归是否会有任何性能提升.任何人都可以告诉我如何使用递归C#函数执行CTE所做的相同工作,假设我有一个IQueryable,其中Tree是一个表示分层表中条目的实体?有点像:

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

很容易看到使用lambda表达式很容易做到这一点.

Mar*_*ell 5

真正的递归lambdas(和推论,Expressions)在技术上是可能的,但几乎是疯了.我还期望任何解析器(L2S,EF等),除了可能 LINQ到对象只是去疯狂试图拆散这一点.

简而言之:你会认为这是一个不受支持的机制Expression.

最后,请注意,仅仅因为你正在写一个Expression并不意味着你在C#中执行它 - 事实上,很可能恰恰相反:如果你正在积极地编写一个Expression(而不是委托或程序代码),我认为它正在进行解析器(除非您曾经.AsQueryable()将它推送到LINQ到对象).


Joh*_*ren 5

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))
        );
}
Run Code Online (Sandbox Code Playgroud)