EnumerableExtension.cs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
  2. //
  3. // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
  4. //
  5. // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
  6. namespace Admin.NET.Core;
  7. /// <summary>
  8. /// 数据集合拓展类
  9. /// </summary>
  10. public static class EnumerableExtension
  11. {
  12. private static readonly ConcurrentDictionary<string, PropertyInfo> PropertyCache = new();
  13. /// <summary>
  14. /// 查询有父子关系的数据集
  15. /// </summary>
  16. /// <param name="list">数据集</param>
  17. /// <param name="idExpression">主键ID字段</param>
  18. /// <param name="parentIdExpression">父级字段</param>
  19. /// <param name="topParentIdValue">顶级节点父级字段值</param>
  20. /// <param name="isContainOneself">是否包含顶级节点本身</param>
  21. /// <returns></returns>
  22. public static IEnumerable<T> ToChildList<T, P>(this IEnumerable<T> list,
  23. Expression<Func<T, P>> idExpression,
  24. Expression<Func<T, P>> parentIdExpression,
  25. object topParentIdValue,
  26. bool isContainOneself = true)
  27. {
  28. if (list == null || !list.Any()) return Enumerable.Empty<T>();
  29. var propId = GetPropertyInfo(idExpression);
  30. var propParentId = GetPropertyInfo(parentIdExpression);
  31. // 查找所有顶级节点
  32. var topNodes = list.Where(item => Equals(propId.GetValue(item), topParentIdValue)).ToList();
  33. return TraverseHierarchy(list, propId, propParentId, topNodes, isContainOneself);
  34. }
  35. /// <summary>
  36. /// 查询有父子关系的数据集
  37. /// </summary>
  38. /// <param name="list">数据集</param>
  39. /// <param name="idExpression">主键ID字段</param>
  40. /// <param name="parentIdExpression">父级字段</param>
  41. /// <param name="topLevelPredicate">顶级节点的选择条件</param>
  42. /// <param name="isContainOneself">是否包含顶级节点本身</param>
  43. /// <returns></returns>
  44. public static IEnumerable<T> ToChildList<T, P>(this IEnumerable<T> list,
  45. Expression<Func<T, P>> idExpression,
  46. Expression<Func<T, P>> parentIdExpression,
  47. Expression<Func<T, bool>> topLevelPredicate,
  48. bool isContainOneself = true)
  49. {
  50. if (list == null || !list.Any()) return Enumerable.Empty<T>();
  51. // 获取顶级节点
  52. var topNodes = list.Where(topLevelPredicate.Compile()).ToList();
  53. if (!topNodes.Any()) return Enumerable.Empty<T>();
  54. var idPropertyInfo = GetPropertyInfo(idExpression);
  55. var parentPropertyInfo = GetPropertyInfo(parentIdExpression);
  56. return TraverseHierarchy(list, idPropertyInfo, parentPropertyInfo, topNodes, isContainOneself);
  57. }
  58. /// <summary>
  59. /// 辅助方法,从表达式中提取属性信息并使用临时缓存
  60. /// </summary>
  61. private static PropertyInfo GetPropertyInfo<T, P>(Expression<Func<T, P>> expression)
  62. {
  63. // 使用 ConcurrentDictionary 确保线程安全
  64. return PropertyCache.GetOrAdd(typeof(T).FullName + "." + ((MemberExpression)expression.Body).Member.Name, k =>
  65. {
  66. if (expression.Body is UnaryExpression { Operand: MemberExpression member }) return (PropertyInfo)member.Member;
  67. if (expression.Body is MemberExpression memberExpression) return (PropertyInfo)memberExpression.Member;
  68. throw Oops.Oh("表达式必须是一个属性访问: " + expression);
  69. });
  70. }
  71. /// <summary>
  72. /// 使用队列遍历层级结构
  73. /// </summary>
  74. private static IEnumerable<T> TraverseHierarchy<T>(IEnumerable<T> list,
  75. PropertyInfo idPropertyInfo,
  76. PropertyInfo parentPropertyInfo,
  77. List<T> topNodes,
  78. bool isContainOneself)
  79. {
  80. var queue = new Queue<T>(topNodes);
  81. var result = new HashSet<T>(topNodes);
  82. while (queue.Count > 0)
  83. {
  84. var currentNode = queue.Dequeue();
  85. var children = list.Where(item => Equals(parentPropertyInfo.GetValue(item), idPropertyInfo.GetValue(currentNode))).ToList();
  86. children.Where(child => result.Add(child)).ForEach(child => queue.Enqueue(child));
  87. }
  88. if (isContainOneself) return result;
  89. // 如果不需要包含顶级节点本身,则移除它们
  90. topNodes.ForEach(e => result.Remove(e));
  91. return result;
  92. }
  93. }