当前位置:   article > 正文

C# linq 根据多字段动态Group by

C# linq 根据多字段动态Group by

实现类: 

  1. public static class LinqHepler
  2. {
  3. /// <summary>
  4. /// 根据单个字段动态Group
  5. /// </summary>
  6. /// <typeparam name="T"></typeparam>
  7. /// <param name="source"></param>
  8. /// <param name="propertyName"></param>
  9. /// <returns></returns>
  10. public static IEnumerable<IGrouping<object, T>> GroupByDynamic<T>(this IEnumerable<T> source, string propertyName)
  11. {
  12. var parameter = Expression.Parameter(typeof(T), "p");
  13. var property = Expression.Property(parameter, propertyName);
  14. var lambda = Expression.Lambda(typeof(Func<T, object>), Expression.Convert(property, typeof(object)), parameter);
  15. var groupByMethod = typeof(Enumerable).GetMethods()
  16. .Where(m => m.Name == "GroupBy" && m.GetParameters().Length == 2).First();
  17. var groupBy = groupByMethod.MakeGenericMethod(typeof(T), typeof(object));
  18. return (IEnumerable<IGrouping<object, T>>)groupBy.Invoke(null, new object[] { source, lambda.Compile() });
  19. }
  20. /// <summary>
  21. /// 多字段动态GroupBy
  22. /// </summary>
  23. /// <typeparam name="T">需要重写GetHashCode和Equals方法, 因为类是比较引用的,而常用于GroupBy操作的匿名类是比较值的</typeparam>
  24. /// <param name="source"></param>
  25. /// <param name="propertyNames"></param>
  26. /// <returns></returns>
  27. public static IEnumerable<IGrouping<T, T>> GroupByDynamic<T>(this IEnumerable<T> source, List<string> propertyNames) where T : class
  28. {
  29. if(propertyNames.IsNullOrEmpty())
  30. throw new ArgumentNullException("propertyNames");
  31. var parameter = Expression.Parameter(typeof(T), "p");
  32. var lambda = BuildSelector<T>(parameter, propertyNames);
  33. var groupByMethod = typeof(Enumerable).GetMethods()
  34. .Where(m => m.Name == "GroupBy" && m.GetParameters().Length == 2).First();
  35. var groupBy = groupByMethod.MakeGenericMethod(typeof(T), typeof(T));
  36. return (IEnumerable<IGrouping<T, T>>)groupBy.Invoke(null, new object[] { source, lambda });
  37. }
  38. private static Func<T, T> BuildSelector<T>(ParameterExpression param, List<string> propertyNames)
  39. {
  40. var memberBindings = new List<MemberBinding>(propertyNames.Count);
  41. foreach (var propertyName in propertyNames)
  42. {
  43. var expressionProperty = Expression.Property(param, propertyName);
  44. memberBindings.Add(Expression.Bind(typeof(T).GetProperty(propertyName), expressionProperty));
  45. }
  46. var memberInit = Expression.MemberInit(Expression.New(typeof(T)), memberBindings);
  47. var lambda = Expression.Lambda<Func<T, T>>(memberInit, param);
  48. return lambda.Compile();
  49. }
  50. }

使用方式:

  1. var people = new List<Person>
  2. {
  3. new Person { Name = "Alice",Six="男", Age = 30 },
  4. new Person { Name = "Alice",Six="男", Age = 20 },
  5. new Person { Name = "Bob",Six="男", Age = 30 },
  6. new Person { Name = "Bob",Six="男", Age = 25 },
  7. new Person { Name = "Charlie",Six="男", Age = 25 },
  8. new Person { Name = "Charlie",Six="男", Age = 25 }
  9. };
  10. var groupFileds = new List<string>() { "Name", "Six" };
  11. var groupedByName = people
  12. .GroupByDynamic(groupFileds)
  13. .Select(t =>
  14. {
  15. var model = new Person()
  16. {
  17. Name = t.First().Name,
  18. Six = t.First().Six,
  19. Age = t.Sum(p => p.Age),
  20. Details = t.ToList(),
  21. };
  22. return model;
  23. })
  24. .ToList();
  25. //需要重写GetHashCode和Equals方法, 因为类是比较引用的,而常用于GroupBy操作的匿名类是比较值的
  26. public class Person
  27. {
  28. public string? Name { get; set; }
  29. public string? Six { get; set; }
  30. public int Age { get; set; }
  31. public override int GetHashCode()
  32. {
  33. return $"{Name},{Six},{Age}".GetHashCode(); //需要用来分组的字段,或者全部字段
  34. }
  35. public override bool Equals(object? obj)
  36. {
  37. if (obj == null)
  38. return false;
  39. if(!(obj is Person))
  40. return false;
  41. return this.GetHashCode() == obj.GetHashCode();
  42. }
  43. }

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
  

闽ICP备14008679号