我使用EntityFramework(版本6)来Map到递归层次结构,它Map得很好。我的问题是,我想递归地获得层次结构中特定节点的ALL子节点。我使用Linq很容易地获得子节点:
var recursiveList = db.ProcessHierarchyItems .Where(x => x.id == id) .SelectMany(x => x.Children);
有谁知道一个干净的实现,将递归地获得所有的孩子?
mpgws1up1#
虽然可以在这里使用递归方法,但您可以使用显式堆栈来遍历此树结构,以避免使用堆栈空间,堆栈空间对于大型树结构并不总是足够的。这样的方法作为迭代器块也非常好,迭代器块在递归时比常规方法昂贵得多,所以这也会表现得更好:
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); } }
zpjtge222#
感谢Servy,我对你的代码进行了一些扩展,以允许迭代单个项目以及集合。我在寻找一种方法来找出一个异常或任何内部异常是否属于某种类型时遇到了这种情况,但这将有很多用途。
这里是一个摆弄的例子,测试用例等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); } } }
polhcujo3#
试试这个。当其他答案在构建可枚举对象时枚举可枚举对象时,我们应该考虑构建它而不枚举它。
public static IEnumerable<T> SelectRecursively<T>(this T source, Func<T, IEnumerable<T>> selector) { return selector(source).SelectMany(x => x.SelectRecursively(selector).Prepend(x)); }
8i9zcol24#
最简单的解决方案似乎是引入递归方法。你不能仅仅通过LINQ本身来实现递归:
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));
q5lcpyga5#
我更喜欢linq的递归方式。
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)); }
5条答案
按热度按时间mpgws1up1#
虽然可以在这里使用递归方法,但您可以使用显式堆栈来遍历此树结构,以避免使用堆栈空间,堆栈空间对于大型树结构并不总是足够的。这样的方法作为迭代器块也非常好,迭代器块在递归时比常规方法昂贵得多,所以这也会表现得更好:
zpjtge222#
感谢Servy,我对你的代码进行了一些扩展,以允许迭代单个项目以及集合。我在寻找一种方法来找出一个异常或任何内部异常是否属于某种类型时遇到了这种情况,但这将有很多用途。
这里是一个摆弄的例子,测试用例等dotnetfiddle LinqTraversal
只有帮手:
polhcujo3#
试试这个。当其他答案在构建可枚举对象时枚举可枚举对象时,我们应该考虑构建它而不枚举它。
8i9zcol24#
最简单的解决方案似乎是引入递归方法。你不能仅仅通过LINQ本身来实现递归:
如果你有延迟加载,那么这应该可以工作:
q5lcpyga5#
我更喜欢linq的递归方式。