如何在C#中将树形结构转换为适配Telerik Grid的对象列表
树形结构扁平化适配Telerik Grid的解决方案
问题背景
基于Blazor和.NET 6构建的应用中,需要将自关联表生成的树形Taxonomy结构,转换为扁平的TaxonomyGrid对象列表,用于对接Telerik Grid组件。
实体定义
树形节点类:
public class Taxonomy { public int Id { get; set; } public string Name { get; set; } public int? ParentId { get; set; } }
目标Grid行对象类:
public class TaxonomyGrid { public int? L1Id { get; set; } public string? L1 { get; set; } public int? L2Id { get; set; } public string? L2 { get; set; } public int? L3Id { get; set; } public string? L3 { get; set; } public int? L4Id { get; set; } public string? L4 { get; set; } public int? L5Id { get; set; } public string? L5 { get; set; } }
当前问题代码
现有递归代码仅能处理首个节点分支,回溯层级时会错误重置层级计数器,导致节点被错误映射到层级1:
public TaxonomyGridDTO GetAllChildren(List<TaxonomyDTO> Taxonomy, TaxonomyGridDTO workingTax = default, int level = 0) { if (Taxonomy == null || !Taxonomy.Any()) return new(); level++; if(workingTax == null) workingTax = new(); foreach (var item in Taxonomy) { switch (level) { case 1: workingTax.L1 = item.Name; workingTax.L1Id = item.TaxonomyId; break; case 2: workingTax.L2 = item.Name; workingTax.L2Id = item.TaxonomyId; item.ChildTaxonomy = TaxonomyList.Where(x => x.ParentId == item.TaxonomyId).ToList(); break; case 3: workingTax.L3 = item.Name; workingTax.L3Id = item.TaxonomyId; item.ChildTaxonomy = TaxonomyList.Where(x => x.ParentId == item.TaxonomyId).ToList(); break; case 4: workingTax.L4 = item.Name; workingTax.L4Id = item.TaxonomyId; item.ChildTaxonomy = TaxonomyList.Where(x => x.ParentId == item.TaxonomyId).ToList(); break; case 5: workingTax.L5 = item.Name; workingTax.L5Id = item.TaxonomyId; break; } if (item.ChildTaxonomy != null && item.ChildTaxonomy.Any()) { GetAllChildren(item.ChildTaxonomy, workingTax, level); } else { TaxonomyGrid.Add(workingTax); workingTax = new(); level = 1; } } return workingTax; }
问题分析
当前代码的核心问题:
- 共享实例导致数据覆盖:递归过程中复用同一个
workingTax实例,不同分支的层级数据会互相干扰。 - 手动重置层级破坏逻辑:回溯时强制将
level设为1,打乱了递归的自然层级跟踪机制。
正确实现方式
方法1:递归+层级快照(推荐)
每个节点基于父节点的TaxonomyGrid创建新实例,保证分支数据独立,同时依靠递归调用栈自然跟踪层级:
// 预分组存储,避免重复查询提升性能 private Dictionary<int?, List<Taxonomy>> _taxonomyByParentId; public List<TaxonomyGrid> FlattenTaxonomy(List<Taxonomy> allTaxonomies) { var result = new List<TaxonomyGrid>(); // 按ParentId分组所有节点 _taxonomyByParentId = allTaxonomies.GroupBy(t => t.ParentId) .ToDictionary(g => g.Key, g => g.ToList()); // 从根节点(ParentId为null)开始处理 var rootNodes = _taxonomyByParentId.GetValueOrDefault(null, new List<Taxonomy>()); foreach (var root in rootNodes) { var rootGridItem = new TaxonomyGrid { L1Id = root.Id, L1 = root.Name }; ProcessChildren(root, rootGridItem, 1, result); } return result; } private void ProcessChildren(Taxonomy parentNode, TaxonomyGrid parentGridItem, int currentLevel, List<TaxonomyGrid> result) { var children = _taxonomyByParentId.GetValueOrDefault(parentNode.Id, new List<Taxonomy>()); if (!children.Any()) { // 无子节点,当前项为最终行,加入结果 result.Add(parentGridItem); return; } foreach (var child in children) { // 复制父节点的层级数据,创建新实例 var childGridItem = new TaxonomyGrid { L1Id = parentGridItem.L1Id, L1 = parentGridItem.L1, L2Id = parentGridItem.L2Id, L2 = parentGridItem.L2, L3Id = parentGridItem.L3Id, L3 = parentGridItem.L3, L4Id = parentGridItem.L4Id, L4 = parentGridItem.L4, L5Id = parentGridItem.L5Id, L5 = parentGridItem.L5 }; // 根据当前层级设置对应字段 switch (currentLevel + 1) { case 2: childGridItem.L2Id = child.Id; childGridItem.L2 = child.Name; break; case 3: childGridItem.L3Id = child.Id; childGridItem.L3 = child.Name; break; case 4: childGridItem.L4Id = child.Id; childGridItem.L4 = child.Name; break; case 5: childGridItem.L5Id = child.Id; childGridItem.L5 = child.Name; break; } // 递归处理子节点,层级+1 ProcessChildren(child, childGridItem, currentLevel + 1, result); } }
方法2:迭代式遍历(适合层级极深场景)
用栈替代递归调用,避免栈溢出风险,逻辑与递归一致:
public List<TaxonomyGrid> FlattenTaxonomyIterative(List<Taxonomy> allTaxonomies) { var result = new List<TaxonomyGrid>(); var taxonomyByParentId = allTaxonomies.GroupBy(t => t.ParentId) .ToDictionary(g => g.Key, g => g.ToList()); // 栈存储待处理的节点、对应Grid项和当前层级 var stack = new Stack<(Taxonomy Node, TaxonomyGrid GridItem, int Level)>(); // 初始化根节点 var rootNodes = taxonomyByParentId.GetValueOrDefault(null, new List<Taxonomy>()); foreach (var root in rootNodes) { stack.Push((root, new TaxonomyGrid { L1Id = root.Id, L1 = root.Name }, 1)); } while (stack.Count > 0) { var (currentNode, currentGridItem, currentLevel) = stack.Pop(); var children = taxonomyByParentId.GetValueOrDefault(currentNode.Id, new List<Taxonomy>()); if (!children.Any()) { result.Add(currentGridItem); continue; } foreach (var child in children) { var childGridItem = new TaxonomyGrid { L1Id = currentGridItem.L1Id, L1 = currentGridItem.L1, L2Id = currentGridItem.L2Id, L2 = currentGridItem.L2, L3Id = currentGridItem.L3Id, L3 = currentGridItem.L3, L4Id = currentGridItem.L4Id, L4 = currentGridItem.L4, L5Id = currentGridItem.L5Id, L5 = currentGridItem.L5 }; switch (currentLevel + 1) { case 2: childGridItem.L2Id = child.Id; childGridItem.L2 = child.Name; break; case 3: childGridItem.L3Id = child.Id; childGridItem.L3 = child.Name; break; case 4: childGridItem.L4Id = child.Id; childGridItem.L4 = child.Name; break; case 5: childGridItem.L5Id = child.Id; childGridItem.L5 = child.Name; break; } stack.Push((child, childGridItem, currentLevel + 1)); } } return result; }
关键优化点
- 预分组数据:提前按
ParentId分组所有节点,避免递归/迭代中重复查询列表,大幅提升性能。 - 独立实例跟踪层级:每个子节点都基于父节点的Grid对象创建新实例,确保不同分支的层级数据互不干扰。
- 自然层级跟踪:依靠递归调用栈或栈结构自动维护层级,移除手动重置层级的错误逻辑。
内容的提问来源于stack exchange,提问作者USMC6072
相关产品推荐
相关产品推荐

