c# - 如何递归加载所有子记录?
问题描述
我有这个项目类:
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>' 不是迭代器接口类型
我尝试过使方法同步,但它是相同的错误消息,只是没有“任务”部分。
我怎样才能让它工作?
解决方案
您可以为此编写简单的扩展:
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();
推荐阅读
- c++ - Do-while循环一次显示2个提示,请回复
- gstreamer - Gstreamer 中的 codec_data 是什么?
- php - 查询不显示一个结果
- git - 从 Github 下载压缩文件后没有文件存在
- pyspark - 获取多列的不同值
- datastage - 如何在数据阶段实现 not regexp_like()
- newrelic - New Relic 中的负 NRQL 平均值(diskFreeBytes)
- r - R - BigQuery:curl::curl_fetch_memory(url,handle = handle)中的错误:无法连接到 www.googleapis.com 端口 443:访问错误
- flutter-web - (Flutter) 如何识别用户在我的 ListView 中单击了 PopupMenuItem 的哪个 ListTile?
- node.js - 如何将 userId 与 post 表链接