递归构建文档树出现重复节点问题的排查与修复求助
文档递归依赖树生成问题修复
问题描述
我有一批基于其他文档生成的文档,尝试通过递归查找它们依赖的源文档,但当前实现存在问题:所有节点都重复合并在一起,无法生成正确的层级结构,期望生成层级分明的文档树结构。核心传递参数为文档类型和子元素列表。
当前实现代码
递归填充方法
public async Task<List<DocumentTreeItem>> FillRecursionTree(Guid documentId, string documentPresentationName, List<DocumentTreeItem> treeItems, Type documentType) { await FillRecursionTree(documentId, documentPresentationName, treeItems, documentType); return treeItems; async Task FillRecursionTree(Guid childrenId, string childrenIdDocumentPresentationName, List<DocumentTreeItem> childrenIdTreeItems, Type childrenIdDocumentType) { var currentNode = new DocumentTreeItem(documentPresentationName, childrenIdDocumentType, childrenIdTreeItems) { Id = childrenId, DocumentPresentationName = childrenIdDocumentPresentationName, TypeDocument = childrenIdDocumentType }; childrenIdTreeItems.Add(currentNode); var customerInvoices = await GetBaseCustomerInvoiceDocuments(childrenId); foreach (var customerInvoice in customerInvoices) { childrenIdDocumentType = customerInvoice.GetType(); await FillRecursionTree(customerInvoice.Id, customerInvoice.DocumentPresentationName, currentNode.Childrens, childrenIdDocumentType); } } }
节点实体类
public Guid Id { get; set; } public string? DocumentPresentationName { get; set; } public Type TypeDocument { get; set; } public List<DocumentTreeItem> Childrens { get; set; } = new List<DocumentTreeItem>(); public DocumentTreeItem(string documentPresentationName, Type typeDocument, List<DocumentTreeItem> children = null) { DocumentPresentationName = documentPresentationName; TypeDocument = typeDocument; if (children != null) Children.AddRange(children); } public List<DocumentTreeItem> Children { get { return Childrens; } }
问题分析
- 构造函数参数错误:创建
currentNode时,将父节点的列表childrenIdTreeItems传入构造函数,导致当前节点的Children直接引用父列表,后续添加节点时会全部混入同一集合,无法形成层级。 - 初始化逻辑混淆:构造函数中把传入的
children添加到自身Children,同时又把currentNode添加到父列表childrenIdTreeItems,形成循环引用,进一步破坏层级结构。 - 命名不统一:实体类中字段
Childrens与属性Children命名冗余,增加代码混淆风险。
修复方案
修正后的节点实体类
统一命名,简化子节点集合的定义:
public Guid Id { get; set; } public string? DocumentPresentationName { get; set; } public Type TypeDocument { get; set; } public List<DocumentTreeItem> Children { get; set; } = new List<DocumentTreeItem>(); public DocumentTreeItem(string documentPresentationName, Type typeDocument) { DocumentPresentationName = documentPresentationName; TypeDocument = typeDocument; }
修正后的递归填充方法
去掉构造函数的子节点参数,确保每个节点的子节点使用自身独立的Children集合,同时修正节点名称的赋值错误:
public async Task<List<DocumentTreeItem>> FillRecursionTree(Guid documentId, string documentPresentationName, Type documentType) { var rootTree = new List<DocumentTreeItem>(); await FillRecursionTreeInternal(documentId, documentPresentationName, rootTree, documentType); return rootTree; async Task FillRecursionTreeInternal(Guid nodeId, string nodeName, List<DocumentTreeItem> parentList, Type nodeType) { // 创建当前节点,子节点集合默认初始化 var currentNode = new DocumentTreeItem(nodeName, nodeType) { Id = nodeId, DocumentPresentationName = nodeName, TypeDocument = nodeType }; // 将当前节点添加到父节点的子集合中 parentList.Add(currentNode); // 递归查找当前节点的依赖文档 var dependentDocuments = await GetBaseCustomerInvoiceDocuments(nodeId); foreach (var doc in dependentDocuments) { await FillRecursionTreeInternal(doc.Id, doc.DocumentPresentationName, currentNode.Children, doc.GetType()); } } }
关键修复点说明
- 移除了实体类构造函数的子节点参数,避免错误引用父集合;
- 递归方法改为内部独立方法,初始调用时创建根列表,确保根节点正确挂载;
- 创建当前节点时使用自身的
Children集合传递给下一层递归,保证层级嵌套; - 修正了节点名称赋值错误(原代码中错误使用外层方法的
documentPresentationName而非当前节点的nodeName)。
内容的提问来源于stack exchange,提问作者hans fridrech
相关产品推荐
相关产品推荐

