表达式树+反射实现动态排序。List动态排序,集合动态排序 电脑版发表于:2022/12/5 15:04 **比如要点击表头进行排序,排序的字段比如多,一个一个判断去写就比较麻烦。判断就太多了,如果其他地方也要用排序重复代码就会很多,类似这种。** ``` if (sort.ToLower() == "max") { if (sortway == "asc") { query = query.OrderBy(a => a.Max); } else { query = query.OrderByDescending(a => a.Max); } } if (sort.ToLower() == "min") { if (sortway == "asc") { query = query.OrderBy(a => a.Min); } else { query = query.OrderByDescending(a => a.Min); } } if (sort.ToLower() == "sum") { if (sortway == "asc") { query = query.OrderBy(a => a.Min); } else { query = query.OrderByDescending(a => a.Min); } } ``` **所以我们封装一下通用的排序方法** ``` using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace SmartEDU.LabRoom.Service.Tools { /// <summary> /// AJ:集合动态排序工具类 /// </summary> public static class OrderTools { public static IEnumerable<TSource> OrderByStr<TSource>(this IEnumerable<TSource> query, string sort, string sortway) { if (!string.IsNullOrEmpty(sort)) { //第一步要拿到排序字段的类型。忽略大小写一下 Type propertyType = typeof(TSource).GetProperty(sort, BindingFlags.Public|BindingFlags.IgnoreCase|BindingFlags.Instance).PropertyType; //通过反射拿到方法 var method = typeof(OrderTools).GetMethod(sortway == "asc" ? "DealAsc" : "DealDesc"); //给反射拿到的方法提供泛型 method = method.MakeGenericMethod(typeof(TSource), propertyType); //反射调用方法 IEnumerable<TSource> result = (IEnumerable<TSource>)method.Invoke(null, new object[] { query, sort }); return result; } return query; } /// <summary> /// 处理升序排序 /// 通过一个方法中转实现类型的传递 /// </summary> public static IEnumerable<TSource> DealAsc<TSource, M>(IEnumerable<TSource> query, string sort) { return query.OrderBy(OrderLamdba<TSource, M>(query, sort).Compile()); } /// <summary> /// 处理降序排序 /// 通过一个方法中转实现类型的传递 /// </summary> public static IEnumerable<TSource> DealDesc<TSource, M>(IEnumerable<TSource> query, string sort) { return query.OrderByDescending(OrderLamdba<TSource, M>(query, sort).Compile()); } static Expression<Func<TSource, M>> OrderLamdba<TSource, M>(IEnumerable<TSource> query, string sort) { var left = Expression.Parameter(typeof(TSource), "a"); var body = Expression.Property(left, sort); Expression<Func<TSource, M>> lamdba = Expression.Lambda<Func<TSource, M>>(body, left); return lamdba; } } } ``` **使用就很方便了** ``` //降序 if (item.IsDesc) { labStatisticsList = labStatisticsList.OrderByStr(item.OrderField, "desc").ToList(); } //升序 else { labStatisticsList = labStatisticsList.OrderByStr(item.OrderField, "asc").ToList(); } ``` 这里升序降序其实都可以不用判断,按照升降序的格式传递就行,我这里是修改同事写的代码,前台传递过来就是这种格式了,就不单独处理了,而且其实这种升降序判断一下代码可读性还高一点。 这里是集合在内存中排序的,如果是ef这类的排序参考:https://www.tnblog.net/aojiancc2/article/details/2752 这个里边封装的也是根据那篇博文稍微修改了一点,其实主要就是把IQueryable换成了IEnumerable **有时候数据量不大的时候前台又需要分页和排序,就可以把数据查询出来,然后在内存中排序之后在分页** ``` // 内存中排序 if (item.IsDesc) { labStatisticsList = labStatisticsList.OrderByStr(item.OrderField, "desc").ToList(); } else { labStatisticsList = labStatisticsList.OrderByStr(item.OrderField, "asc").ToList(); } //在内存中排序后在分页 labStatisticsList = labStatisticsList.Skip((quer.PageIndex - 1) * quer.PageSize).Take(quer.PageSize).ToList(); ``` 当然这只是一种方案,一般页数只有几页的时候可以使用,数据量大了还是要在数据库中分页。有时候去修改其他人写的代码不要去动太多逻辑的时候也可以考虑在内存中分页。