You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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;
}

问题分析

当前代码的核心问题:

  1. 共享实例导致数据覆盖:递归过程中复用同一个workingTax实例,不同分支的层级数据会互相干扰。
  2. 手动重置层级破坏逻辑:回溯时强制将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;
}

关键优化点

  1. 预分组数据:提前按ParentId分组所有节点,避免递归/迭代中重复查询列表,大幅提升性能。
  2. 独立实例跟踪层级:每个子节点都基于父节点的Grid对象创建新实例,确保不同分支的层级数据互不干扰。
  3. 自然层级跟踪:依靠递归调用栈或栈结构自动维护层级,移除手动重置层级的错误逻辑。

内容的提问来源于stack exchange,提问作者USMC6072

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 09:45:00