Linq Filter设计中OrderBy选择器非字符串类型报错的类型转换方案
解决方案
你遇到的问题本质是泛型参数TKey在接口层被类型擦除导致无法直接调用泛型排序方法,枚举所有类型的方案可维护性极差,新增类型就要修改代码,推荐通过反射动态构造泛型排序方法调用解决该问题。
第一步:实现通用排序扩展方法
新增一个静态扩展类,封装动态调用排序方法的逻辑:
using System.Linq.Expressions; using System.Reflection; public static class QueryableOrderExtensions { // 预获取Queryable的四个排序方法定义 private static readonly MethodInfo _orderByMethod = typeof(Queryable) .GetMethods(BindingFlags.Public | BindingFlags.Static) .Single(m => m.Name == nameof(Queryable.OrderBy) && m.GetParameters().Length == 2); private static readonly MethodInfo _orderByDescMethod = typeof(Queryable) .GetMethods(BindingFlags.Public | BindingFlags.Static) .Single(m => m.Name == nameof(Queryable.OrderByDescending) && m.GetParameters().Length == 2); private static readonly MethodInfo _thenByMethod = typeof(Queryable) .GetMethods(BindingFlags.Public | BindingFlags.Static) .Single(m => m.Name == nameof(Queryable.ThenBy) && m.GetParameters().Length == 2); private static readonly MethodInfo _thenByDescMethod = typeof(Queryable) .GetMethods(BindingFlags.Public | BindingFlags.Static) .Single(m => m.Name == nameof(Queryable.ThenByDescending) && m.GetParameters().Length == 2); public static IQueryable<T> ApplySort<T>(this IQueryable<T> query, LambdaExpression sortSelector, OrderByDirection direction, bool isSubsequentSort = false) { if (query == null) throw new ArgumentNullException(nameof(query)); if (sortSelector == null) throw new ArgumentNullException(nameof(sortSelector)); // 解包表达式中为了类型兼容添加的object转换 if (sortSelector.Body.NodeType == ExpressionType.Convert) { var actualBody = ((UnaryExpression)sortSelector.Body).Operand; sortSelector = Expression.Lambda(actualBody, sortSelector.Parameters); } // 获取排序字段的实际类型 var keyType = sortSelector.ReturnType; // 构造对应泛型方法的实例 MethodInfo genericSortMethod = isSubsequentSort switch { false when direction == OrderByDirection.Ascending => _orderByMethod.MakeGenericMethod(typeof(T), keyType), false when direction == OrderByDirection.Descending => _orderByDescMethod.MakeGenericMethod(typeof(T), keyType), true when direction == OrderByDirection.Ascending => _thenByMethod.MakeGenericMethod(typeof(T), keyType), true when direction == OrderByDirection.Descending => _thenByDescMethod.MakeGenericMethod(typeof(T), keyType), _ => throw new ArgumentOutOfRangeException(nameof(direction)) }; // 调用排序方法返回新的查询对象 return (IQueryable<T>)genericSortMethod.Invoke(null, new object[] { query, sortSelector })!; } }
第二步:替换原有枚举类型的逻辑
你原来的排序处理循环可以全部替换为以下代码,不需要再枚举任何类型:
// 处理主排序 if (spec.Filter.OrderBy != null) { query = query.ApplySort(spec.Filter.OrderBy.Selector, spec.Filter.OrderBy.Direction); } // 处理多级后续排序 foreach (var thenSort in spec.Filter.ThenBy) { query = query.ApplySort(thenSort.Selector, thenSort.Direction, isSubsequentSort: true); }
方案说明
该方案不需要提前枚举所有可能的排序字段类型,不管是系统内置的值类型、引用类型还是自定义的枚举、复杂值类型都可以直接支持,动态构造的泛型方法不会丢失类型信息,EF Core等ORM可以正常解析生成正确的SQL排序语句,后续新增类型也不需要修改排序逻辑代码。
内容的提问来源于stack exchange,提问作者Jens Bright
相关产品推荐
相关产品推荐

