如何递归加载所有子记录? [英] How can I load all child records recursively?

查看:80
本文介绍了如何递归加载所有子记录?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个Project课程:

I have this Project class:

public class Project
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public List<Project> ChildProjects { get; set; }
    // more properties
}

这是我加载任何给定项目的所有后代的尝试:

This is my attempt to load all descendants of any given project:

private async Task<List<Project>> LoadDescendantsOf(Project project)
{
    project.ChildProjects = await db.Projects
        .Where(p => p.ParentId == project.Id)
        .ToListAsync();
    foreach (Project childProject in project.ChildProjects)
    {
        yield return childProject.ChildProjects = 
            await LoadDescendantsOf(childProject);
    }
}

...但是它不起作用.我收到的错误消息是

... but it's not working. The error message I get is

'ProjectsController.LoadDescendantsOf(Project)'的主体不能是迭代器块,因为'Task>'不是迭代器接口类型

The body of 'ProjectsController.LoadDescendantsOf(Project)' cannot be an iterator block because 'Task>' is not an iterator interface type

我尝试使该方法同步,但是它是相同的错误消息,只是没有任务"部分.

I have tried making the method synchronous, but it's the same error message, only without the "Task"-part.

我如何使它工作?

推荐答案

您可以为此编写简单的扩展名:

You can write simple extension for this:

    public static IEnumerable<T> Traverse<T>(this T e, Func<T, IEnumerable<T>> childrenProvider)
    {
        return TraverseMany(new[] { e }, childrenProvider);
    }

    public static IEnumerable<T> TraverseMany<T>(this IEnumerable<T> collection, Func<T, IEnumerable<T>> childrenProvider)
    {
        var stack = new Stack<T>();
        foreach(var c in collection)
        {
            stack.Push(c);
        }
        while (stack.Count > 0)
        {
            var i = stack.Pop();
            yield return i;
            var children = childrenProvider(i);
            if (children != null)
            {
                foreach (var c in children)
                {
                    stack.Push(c);
                }
            }
        }
    }

和用法:

var allProjectIds = p.Traverse(x => x.ChildProjects).Select(x => x.Id).ToList();

如果您要加载子项目,我建议为此在游标上编写递归SQL过程,但这也适用于小数据:

If you want to load subprojects I would recommend write recursive SQL procedure on cursors for this, but this will also do nice on small data:

var allProjectIds = p
    .Traverse(x => 
    {
       x.ChildProjects = db.Projects
                           .Where(p => p.ParentId == project.Id)
                           .ToList();
       return x.ChildProjects;
    })
    .Select(x => x.Id)
    .ToList();

这篇关于如何递归加载所有子记录?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆