如何用Entity LINQ按ID和深度层级筛选递归子节点结果
树形节点的层级筛选解决方案(Entity Framework LINQ)
嘿,我来帮你搞定这个问题!看起来你需要在Entity Framework中实现基于指定节点和深度的树形结构筛选,我之前也处理过类似的需求,给你两种实用的方案:
先明确实体类
首先假设你的数据库表对应的EF实体类是这样的(和你的表结构对应):
public class TreeNode { public int Id { get; set; } public int? ParentId { get; set; } }
方案1:用EF Core递归CTE(推荐,性能最优)
如果你用的是EF Core 3.0及以上版本,递归CTE(公共表表达式)是最高效的方式,它能在单次数据库查询中完成层级遍历:
public IEnumerable<TreeNode> GetNodesByDepth(int targetId, int depth) { using var context = new YourDbContext(); // 替换成你的DbContext类 // 递归CTE查询,完全匹配你的示例需求 var query = context.TreeNodes .FromSqlRaw($@" WITH RecursiveNodeHierarchy AS ( -- 第一步:获取目标节点的直接子节点(对应示例中的level 1) SELECT ParentId, Id, 1 AS CurrentLevel FROM TreeNodes WHERE ParentId = {targetId} UNION ALL -- 第二步:递归获取下一级子节点,直到达到指定深度 SELECT tn.ParentId, tn.Id, rnh.CurrentLevel + 1 FROM TreeNodes tn JOIN RecursiveNodeHierarchy rnh ON tn.ParentId = rnh.Id WHERE rnh.CurrentLevel + 1 <= {depth} ) SELECT ParentId, Id FROM RecursiveNodeHierarchy ") .ToList(); return query; }
对应你的示例验证:
- 当传入
targetId=2、depth=2时:- 先获取
ParentId=2的节点(也就是ID=3,CurrentLevel=1) - 再递归获取
ParentId=3的节点(ID=4、5、6,CurrentLevel=2)
最终结果完全匹配你给出的示例。
- 先获取
- 当传入
targetId=3、depth=2时:- 先获取
ParentId=3的节点(ID=4、5、6,CurrentLevel=1) - 再递归获取
ParentId=4的节点(ID=7,CurrentLevel=2)
正好是你要的结果。
- 先获取
方案2:纯LINQ递归(兼容所有EF版本,适合小数据量)
如果你的项目用的是旧版EF,或者不想写原生SQL,可以用纯LINQ的递归方法。不过要注意:这种方法会多次查询数据库,数据量大时性能会受影响。
public IEnumerable<TreeNode> GetNodesByDepth(int targetId, int depth) { using var context = new YourDbContext(); var result = new List<TreeNode>(); // 先获取目标节点的第一层子节点 var firstLevelChildren = context.TreeNodes.Where(n => n.ParentId == targetId).ToList(); result.AddRange(firstLevelChildren); // 递归获取后续层级的子节点 foreach (var child in firstLevelChildren) { GetChildNodesRecursive(context, child.Id, depth - 1, result); } return result; } private void GetChildNodesRecursive(YourDbContext context, int parentId, int remainingDepth, List<TreeNode> result) { if (remainingDepth <= 1) return; // 已经达到指定深度,停止递归 var children = context.TreeNodes.Where(n => n.ParentId == parentId).ToList(); result.AddRange(children); foreach (var child in children) { GetChildNodesRecursive(context, child.Id, remainingDepth - 1, result); } }
说明:
- 这里的
remainingDepth初始是depth,每递归一层减1,直到剩余深度为1时停止(因为第一层已经获取过了)。 - 这个方法逻辑直观,适合节点数量不多的场景。
额外提示
如果你需要包含目标节点本身(比如深度层级包含目标节点在内),只需要调整CTE的初始查询部分:
-- 初始查询改为获取目标节点本身,层级设为0 SELECT ParentId, Id, 0 AS CurrentLevel FROM TreeNodes WHERE Id = {targetId} -- 递归条件改为 CurrentLevel + 1 < {depth} WHERE rnh.CurrentLevel + 1 < {depth}
这样就能包含目标节点,以及之后depth-1层的所有子节点。
内容的提问来源于stack exchange,提问作者user998405
相关产品推荐
相关产品推荐

