如何用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
相关产品推荐
相关产品推荐

