如何向C# Dictionary值追加数组并实现PrestaShop NTree计算逻辑
需求1:向Dictionary<int, ComputeNTree>指定键追加ComputeNTreeIndexes数组元素
原示例代码存在两个需要修正的问题:
- Dictionary不允许重复键调用Add方法,会抛出异常,需先判断键是否存在再操作
- ComputeNTree的ComputeNTreeIndexes属性需要初始化,否则为null无法追加元素
修正后的类定义与实现代码
public class ComputeNTree { // 推荐用List更方便追加,若必须用数组可保留,追加时转换即可 public List<ComputeNTreeIndexes> ComputeNTreeIndexes { get; set; } = new List<ComputeNTreeIndexes>(); } public class ComputeNTreeIndexes { public int IdCategory { get; set; } public int Nleft { get; set; } public int Nright { get; set; } }
追加元素逻辑
var dict = new Dictionary<int, ComputeNTree>(); int targetKey = 1; // 先判断键是否存在,不存在则初始化 if (!dict.ContainsKey(targetKey)) { dict[targetKey] = new ComputeNTree(); } // 追加3个元素的示例 dict[targetKey].ComputeNTreeIndexes.AddRange(new List<ComputeNTreeIndexes> { new ComputeNTreeIndexes{ IdCategory = 12, Nleft = 1, Nright = 0 }, new ComputeNTreeIndexes{ IdCategory = 14, Nleft = 2, Nright = 0 }, new ComputeNTreeIndexes{ IdCategory = 16, Nleft = 4, Nright = 0 } }); // 若必须保留数组类型的属性,追加逻辑如下: // dict[targetKey].ComputeNTreeIndexes = dict[targetKey].ComputeNTreeIndexes // .Concat(new[] // { // new ComputeNTreeIndexes{ IdCategory = 12, Nleft = 1, Nright = 0 }, // new ComputeNTreeIndexes{ IdCategory = 14, Nleft = 2, Nright = 0 }, // new ComputeNTreeIndexes{ IdCategory = 16, Nleft = 4, Nright = 0 } // }).ToArray();
需求2:PrestaShop嵌套集重建逻辑C#实现
核心逻辑完全对齐PHP版本的前序遍历规则,左值进入节点时赋值,右值在所有子节点遍历完成后赋值,和PrestaShop原生逻辑一致。
核心递归函数实现
/// <summary> /// 递归计算嵌套集左右值 /// </summary> /// <param name="categories">父分类ID对应子分类ID列表的映射,对应PHP的$categoriesArray</param> /// <param name="idCategory">当前计算的分类ID</param> /// <param name="n">左右值计数器,按引用传递</param> /// <returns>所有分类的ID、左值、右值集合</returns> protected static List<(int IdCategory, int Nleft, int Nright)> ComputeNTreeInfos( Dictionary<int, List<int>> categories, int idCategory, ref int n) { var result = new List<(int, int, int)>(); int left = n++; // 存在子分类则递归遍历 if (categories.TryGetValue(idCategory, out var subCategories)) { foreach (var subId in subCategories) { result.AddRange(ComputeNTreeInfos(categories, subId, ref n)); } } int right = n++; result.Add((idCategory, left, right)); return result; }
完整嵌套集重建逻辑
public static void RegenerateEntireNtree() { // 替换为项目自身的店铺ID获取逻辑,对应PHP的上下文读取和配置读取 int idShop = GetCurrentShopId() ?? GetDefaultShopId(); // 1. 从数据库读取分类数据,对应PHP的SQL查询逻辑,实际项目替换为自身的ORM/ADO查询 var categoryList = _dbContext.CategoryShop .Join(_dbContext.Categories, cs => cs.IdCategory, c => c.IdCategory, (cs, c) => new { c.IdCategory, c.IdParent, cs.Position }) .Where(cs => cs.IdShop == idShop) .OrderBy(c => c.IdParent) .ThenBy(c => c.Position) .Select(c => new { c.IdCategory, c.IdParent }) .ToList(); // 2. 组装父分类对应子分类列表的映射,对应PHP的$categoriesArray组装 var categoriesMap = new Dictionary<int, List<int>>(); foreach (var cate in categoryList) { if (!categoriesMap.ContainsKey(cate.IdParent)) { categoriesMap[cate.IdParent] = new List<int>(); } categoriesMap[cate.IdParent].Add(cate.IdCategory); } // 3. 递归计算左右值 if (categoriesMap.TryGetValue(0, out var rootCategories) && rootCategories.Any()) { int n = 1; // 取根节点第一个分类开始计算,和PHP逻辑一致 var nTreeData = ComputeNTreeInfos(categoriesMap, rootCategories[0], ref n); // 4. 分批写入数据库,对应PHP的分块插入逻辑 const int batchSize = 5000; for (int i = 0; i < nTreeData.Count; i += batchSize) { var batch = nTreeData.Skip(i).Take(batchSize).ToList(); // 实际项目建议使用参数化查询/ORM批量扩展,避免SQL注入风险 var values = string.Join(",", batch.Select(x => $"({x.IdCategory},{x.Nleft},{x.Nright})")); var sql = $@"INSERT INTO category (id_category, nleft, nright) VALUES {values} ON DUPLICATE KEY UPDATE nleft=VALUES(nleft), nright=VALUES(nright)"; _dbContext.Database.ExecuteSqlRaw(sql); } } }
内容的提问来源于stack exchange,提问作者Paul Albers
相关产品推荐
相关产品推荐

