You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用LINQ递归获取所有匹配节点?三级场景C#实现咨询

LINQ递归获取多级匹配节点(含三级场景)实现方案

嘿,刚好你提到已经用SQL CTE搞定了递归查询的需求,现在想在C#里用LINQ实现对吧?完全可以!虽然LINQ没有原生的递归运算符,但我们可以自己封装扩展方法来实现,不管是三级还是更深层级的场景都能hold住。

第一步:定义节点模型

首先我们需要一个基础的节点类来模拟你的层级数据,比如:

public class Node
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public string Name { get; set; }
    // 这里可以添加你业务需要的其他属性
}

第二步:封装LINQ递归扩展方法

我们可以写一个通用的扩展方法,让它能像LINQ原生方法一样被调用,实现递归遍历:

public static class LinqExtensions
{
    // 从单个节点出发,递归获取所有后代(包括自身)
    public static IEnumerable<Node> GetAllDescendants(this Node node, IEnumerable<Node> allNodes)
    {
        // 先返回当前节点
        yield return node;
        // 找到当前节点的直接子节点
        var children = allNodes.Where(n => n.ParentId == node.Id);
        // 递归遍历每个子节点的后代
        foreach (var child in children)
        {
            foreach (var descendant in child.GetAllDescendants(allNodes))
            {
                yield return descendant;
            }
        }
    }

    // 从节点集合出发,根据父节点ID获取所有后代
    public static IEnumerable<Node> GetAllDescendants(this IEnumerable<Node> allNodes, int parentId)
    {
        var parentNode = allNodes.FirstOrDefault(n => n.Id == parentId);
        if (parentNode == null) yield break;
        
        foreach (var node in parentNode.GetAllDescendants(allNodes))
        {
            yield return node;
        }
    }
}

第三步:三级场景实战示例

假设我们有一个包含三级节点的数据集,用上面的方法就能轻松获取所有层级的节点:

// 模拟三级节点数据
var allNodes = new List<Node>
{
    new Node { Id = 1, ParentId = null, Name = "一级节点-根" },
    new Node { Id = 2, ParentId = 1, Name = "二级节点-子节点1" },
    new Node { Id = 3, ParentId = 2, Name = "三级节点-孙节点1" },
    new Node { Id = 4, ParentId = 2, Name = "三级节点-孙节点2" },
    new Node { Id = 5, ParentId = 1, Name = "二级节点-子节点2" },
    new Node { Id = 6, ParentId = 5, Name = "三级节点-孙节点3" }
};

// 获取根节点(Id=1)的所有后代(包含自身,覆盖三级全部节点)
var allThreeLevelNodes = allNodes.GetAllDescendants(1);

// 输出结果
foreach (var node in allThreeLevelNodes)
{
    Console.WriteLine($"{node.Name} (ID: {node.Id})");
}

第四步:带筛选条件的递归查询

如果需要只获取符合特定条件的节点(比如名称包含某个关键词),我们可以扩展一个带谓词的方法:

public static class LinqExtensions
{
    // 递归获取符合条件的所有后代节点(包括自身如果符合条件)
    public static IEnumerable<Node> GetMatchingDescendants(this Node node, IEnumerable<Node> allNodes, Func<Node, bool> predicate)
    {
        // 如果当前节点符合条件,返回它
        if (predicate(node))
        {
            yield return node;
        }
        // 递归处理子节点
        var children = allNodes.Where(n => n.ParentId == node.Id);
        foreach (var child in children)
        {
            foreach (var descendant in child.GetMatchingDescendants(allNodes, predicate))
            {
                yield return descendant;
            }
        }
    }
}

// 用法示例:获取所有名称包含"孙节点"的三级匹配节点
var matchingNodes = allNodes.First(n => n.Id == 1)
                            .GetMatchingDescendants(allNodes, n => n.Name.Contains("孙节点"));

和SQL CTE的思路对比

其实这个LINQ递归的逻辑和你之前用的SQL CTE是完全对应的:

  • SQL CTE里先定义初始的根节点集合,然后递归关联子节点;
  • LINQ方法里先返回当前节点,再递归遍历子节点的后代,本质都是自顶向下的递归遍历,逻辑完全相通。

这种方法是延迟执行的,和LINQ的原生特性一致,数据量不大的场景下性能完全够用;如果是超大规模的数据集,可以考虑提前缓存父节点映射来优化查询效率。

内容的提问来源于stack exchange,提问作者USER_8675309

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:25:29