recursion层次 – 使用Linq的recursion查询

我正在使用entity framework(版本6)映射到一个recursion层次结构,并很好地映射。

我的问题是,我想要recursion地获取层次结构中特定节点的所有子节点。

我很容易使用Linq获得子节点:

var recursiveList = db.ProcessHierarchyItems .Where(x => x.id == id) .SelectMany(x => x.Children); 

有谁知道一个干净的实现,这将recursion获得所有的孩子?

谢谢,基思。

虽然在这里可以使用recursion方法,但是您可以使用显式堆栈来遍历此树结构,以避免使用堆栈空间,这对于大型树结构来说并不总是足够的。 这样的方法作为一个迭代器块也是非常好的,迭代器块比常规方法recursion时要便宜得多,所以这样做也会更好:

 public static IEnumerable<T> Traverse<T>(this IEnumerable<T> items, Func<T, IEnumerable<T>> childSelector) { var stack = new Stack<T>(items); while(stack.Any()) { var next = stack.Pop(); yield return next; foreach(var child in childSelector(next)) stack.Push(child); } } 

谢谢Servy ,我扩展了你的代码以允许迭代单个项目以及集合。 当我寻找一种方法来找出一个exception或任何内部exception是否属于某种types时,我会遇到这种情况,但这会有很多用途。

这里是一个小例子,testing用例等dotnetfiddle LinqTraversal

只是帮手:

 public static class LinqRecursiveHelper { /// <summary> /// Return item and all children recursively. /// </summary> /// <typeparam name="T">Type of item.</typeparam> /// <param name="item">The item to be traversed.</param> /// <param name="childSelector">Child property selector.</param> /// <returns></returns> public static IEnumerable<T> Traverse<T>(this T item, Func<T, T> childSelector) { var stack = new Stack<T>(new T[] { item }); while (stack.Any()) { var next = stack.Pop(); if (next != null) { yield return next; stack.Push(childSelector(next)); } } } /// <summary> /// Return item and all children recursively. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="item"></param> /// <param name="childSelector"></param> /// <returns></returns> public static IEnumerable<T> Traverse<T>(this T item, Func<T, IEnumerable<T>> childSelector) { var stack = new Stack<T>(new T[] { item }); while (stack.Any()) { var next = stack.Pop(); //if(next != null) //{ yield return next; foreach (var child in childSelector(next)) { stack.Push(child); } //} } } /// <summary> /// Return item and all children recursively. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="items"></param> /// <param name="childSelector"></param> /// <returns></returns> public static IEnumerable<T> Traverse<T>(this IEnumerable<T> items, Func<T, IEnumerable<T>> childSelector) { var stack = new Stack<T>(items); while (stack.Any()) { var next = stack.Pop(); yield return next; foreach (var child in childSelector(next)) stack.Push(child); } } } 

最简单的解决scheme似乎引入了recursion方法。 LINQ本身不能recursion:

 IEnumerable<X> GetChildren(X x) { foreach (var rChild in x.Children.SelectMany(child => GetChildren(child))) { yield return rChild; } } 

如果你有懒加载,那么这应该工作:

 var recursiveList = db.ProcessHierarchyItems .Where(x => x.id == id) .AsEnumerable() .SelectMany(x => GetChildren(x)); 

我更喜欢linq的方式做recursion。

 public static IEnumerable<TReturn> Recursive<TItem, TReturn>(this TItem item, Func<TItem, IEnumerable<TReturn>> select, Func<TItem, IEnumerable<TItem>> recurrence) { return select(item).Union(recurrence(item).Recursive(select, recurrence)); }