Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 递归层次结构-使用Linq的递归查询_C#_Linq_Entity Framework_Recursion - Fatal编程技术网

C# 递归层次结构-使用Linq的递归查询

C# 递归层次结构-使用Linq的递归查询,c#,linq,entity-framework,recursion,C#,Linq,Entity Framework,Recursion,我使用实体框架(版本6)映射到递归层次结构,它映射得很好 我的问题是,我希望递归地获取层次结构中特定节点的所有子节点 我使用Linq很容易获得子节点: var recursiveList = db.ProcessHierarchyItems .Where(x => x.id == id) .SelectMany(x => x.Children); 有谁知道一个干净的实现,它将递归地获取所有子对象吗?最简单的解决方案似乎引入了递归方法。

我使用实体框架(版本6)映射到递归层次结构,它映射得很好

我的问题是,我希望递归地获取层次结构中特定节点的所有子节点

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

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

有谁知道一个干净的实现,它将递归地获取所有子对象吗?

最简单的解决方案似乎引入了递归方法。仅凭LINQ本身无法实现递归:

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

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

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);
    }
}
公共静态IEnumerable遍历(此IEnumerable项,
Func(儿童选择器)
{
var堆栈=新堆栈(项目);
while(stack.Any())
{
var next=stack.Pop();
其次是收益率;
foreach(childSelector中的var child(下一个))
栈.推(子);
}
}

我更喜欢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));
    }
public static IEnumerable Recursive(此项、Func select、Func Recursive)
{
返回select(item).Union(递归(item).Recursive(select,Recursive));
}

谢谢Servy,我对您的代码进行了一些扩展,以允许迭代单个项目和集合。我是在寻找一种方法来确定一个异常或任何内部异常是否属于某一类型时遇到的,但这有很多用途

下面是一些示例、测试用例等。

只有帮手:

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);
        }
    }
}
公共静态类LinqRecursiveHelper
{
/// 
///递归返回项和所有子项。
/// 
///项目类型。
///要遍历的项。
///子属性选择器。
/// 
公共静态IEnumerable遍历(此T项,Func childSelector)
{
var stack=新堆栈(新的T[]{item});
while(stack.Any())
{
var next=stack.Pop();
如果(下一步!=null)
{
其次是收益率;
stack.Push(childSelector(next));
}
}
}
/// 
///递归返回项和所有子项。
/// 
/// 
/// 
/// 
/// 
公共静态IEnumerable遍历(此T项,Func childSelector)
{
var stack=新堆栈(新的T[]{item});
while(stack.Any())
{
var next=stack.Pop();
//如果(下一步!=null)
//{
其次是收益率;
foreach(childSelector中的var child(下一个))
{
栈.推(子);
}
//}
}
}
/// 
///递归返回项和所有子项。
/// 
/// 
/// 
/// 
/// 
公共静态IEnumerable遍历(此IEnumerable项,
Func(儿童选择器)
{
var堆栈=新堆栈(项目);
while(stack.Any())
{
var next=stack.Pop();
其次是收益率;
foreach(childSelector中的var child(下一个))
栈.推(子);
}
}
}

试试这个。当其他答案在枚举的同时枚举枚举时,我们应该考虑在不枚举的情况下构建它。
public static IEnumerable<T> SelectRecursively<T>(this T source, Func<T, IEnumerable<T>> selector)
{
        return selector(source).SelectMany(x => x.SelectRecursively(selector).Prepend(x));
}
公共静态IEnumerable SelectiveRecursive(此T源,函数选择器)
{
返回选择器(源).SelectMany(x=>x.SelectRecursively(选择器).Prepend(x));
}

我将GetChildren定义为:IEnumerable GetChildren(ProcessHierarchyItem x)-但是我得到一个错误:“LINQ to Entities无法识别方法'System.Collections.Generic.IEnumerable`1[RedowMVC.Models.ProcessHierarchyItem]GetChildren(RedowMVC.Models.ProcessHierarchyItem)“方法,而此方法无法转换为存储表达式。”@user3168594哦,在本例中,我已将您的问题标记为重复问题。请阅读您的问题下自动发布的链接中的答案。@user3168594您也可以尝试我的简单修复-查看编辑后的答案。感谢回复-您的更改消除了错误,但不会返回任何错误children@gt是 啊这取决于您是否只需要叶节点或中间节点。感谢的可能重复-您能否提供一个如何调用此方法的示例。我已经尝试过:var-degents=db.ProcessHierarchyItems.Traverse(x=>x.Id==Id)@user3168594 lambda需要根据签名返回该节点的所有子节点,因此根据您的问题,它看起来应该是:
x=>x.children
几乎正常工作-我的linq现在是:'var-grounds=db.ProcessHierarchyItems.Where(x=>x.id==idOfParentNode.transverse(x=>x.children);'-Traverse现在不仅带回了所有的孩子,还带回了父母——这是正确的吗?@user3168594非常欢迎您调整代码以满足您自己的需要;我相信您有能力在给出此答案的情况下找到解决方案。@Maxim此方法接受
IEnumerable
,而不是
IQueryable
,因此仅对内存集合进行操作。无法将其转换为DB查询。不确定我是否遵守了这里的适当礼仪。我不知道编辑是否比基于另一个答案的新答案更合适。好吧,我觉得我是在用这个重新发明轮子,我是。结合这个答案,您可以只使用SelectMany返回子项,并且SelectMany()。Contat(root)和SelectMany不是递归的,所以我的原始解决方案是有效的。我尝试了您的解决方案,并对其进行了一些调整,以支持可为空的子项。也许你应该更新你的答案,以帮助其他人面对同样的空问题,但做得好!它看起来很酷,但我有点困惑
public static IEnumerable<T> SelectRecursively<T>(this T source, Func<T, IEnumerable<T>> selector)
{
        return selector(source).SelectMany(x => x.SelectRecursively(selector).Prepend(x));
}