【问题标题】:How can I load all child records recursively?如何递归加载所有子记录?
【发布时间】:2020-02-10 12:35:43
【问题描述】:

我有这个项目类:

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

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

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);
    }
}

...但它不起作用。我得到的错误信息是

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

我尝试过使方法同步,但它是相同的错误消息,只是没有“任务”部分。

我怎样才能让它工作?

【问题讨论】:

  • 不知道是否可以结合任务使用yield,但是yield只有在返回类型为IEnumerable&lt;T&gt;时才有效。所以你可以尝试将返回类型更改为Task&lt;IEnumerable&lt;Project&gt;&gt;
  • yield return childProject.ChildProjects = await LoadDescendantsOf(childProject); 有点奇怪。

标签: c# recursion entity-framework-core yield-return


【解决方案1】:

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

    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 过程,但这也适用于小数据:

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

【讨论】:

  • 你的TraverseMany-方法有错误吗?我在e 上收到编译时错误 - “当前上下文中不存在”。
  • 感谢您指出这一点。我没有编译它。修复编译错误。
  • 您的用法示例中的p 是什么?
  • 它是任何给定的项目。
  • 第二个用法示例我似乎陷入了无限循环。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-31
  • 2011-02-04
  • 1970-01-01
  • 1970-01-01
  • 2011-02-19
  • 1970-01-01
  • 2017-06-17
相关资源
最近更新 更多