如何创建带泛型参数的keySelector选择器函数作为变量用于Linq排序?
错误原因
你遇到的编译错误核心是:TKey是调用方指定的泛型参数,但不同属性返回的类型(字符串/整数/布尔值)是固定的,无法和任意传入的TKey类型匹配,即使用强制类型转换也无法解决类型不匹配的根本问题。
可行方案
方案1:封装完整排序逻辑(推荐,性能最好)
不需要单独返回keySelector,直接把排序操作整体封装,一次switch逻辑覆盖四个排序方法的需求:
// 首次排序扩展方法 public static IOrderedQueryable<TSource> ApplySort<TSource>(this IQueryable<TSource> source, string propertyName, bool isDescending = false) where TSource : MyEntity { return propertyName switch { "propa" => isDescending ? source.OrderByDescending(e => e.MyPropertyA) : source.OrderBy(e => e.MyPropertyA), "propb" => isDescending ? source.OrderByDescending(e => e.MyPropertyB) : source.OrderBy(e => e.MyPropertyB), "propc" => isDescending ? source.OrderByDescending(e => e.MyPropertyC) : source.OrderBy(e => e.MyPropertyC), // 其余6个属性直接在这里加就行 _ => throw new ArgumentException($"Unknown property: {propertyName}", nameof(propertyName)) }; } // 后续多级排序扩展方法(对应ThenBy/ThenByDescending) public static IOrderedQueryable<TSource> ApplyThenSort<TSource>(this IOrderedQueryable<TSource> source, string propertyName, bool isDescending = false) where TSource : MyEntity { return propertyName switch { "propa" => isDescending ? source.ThenByDescending(e => e.MyPropertyA) : source.ThenBy(e => e.MyPropertyA), "propb" => isDescending ? source.ThenByDescending(e => e.MyPropertyB) : source.ThenBy(e => e.MyPropertyB), "propc" => isDescending ? source.ThenByDescending(e => e.MyPropertyC) : source.ThenBy(e => e.MyPropertyC), _ => throw new ArgumentException($"Unknown property: {propertyName}", nameof(propertyName)) }; }
使用示例:
// 先按propa升序,再按propb降序 var query = dbContext.MyEntities .ApplySort("propa") .ApplyThenSort("propb", isDescending: true);
这种方式你只需要写一次9个属性的switch逻辑,就能覆盖所有排序场景,没有额外性能开销,所有LINQ提供器都能正常兼容。
方案2:动态构建表达式树(适合属性数量多、后续会扩展的场景)
如果不想每次加属性都改switch,可以通过表达式树API动态生成keySelector,自动适配属性类型:
// 生成属性选择表达式 private static LambdaExpression GetKeySelector<TSource>(string propertyName) where TSource : MyEntity { var parameter = Expression.Parameter(typeof(TSource), "e"); var propertyAccess = Expression.Property(parameter, propertyName); return Expression.Lambda(propertyAccess, parameter); } // 动态调用排序方法 public static IOrderedQueryable<TSource> OrderByDynamic<TSource>(this IQueryable<TSource> source, string propertyName, bool isDescending = false) where TSource : MyEntity { var keySelector = GetKeySelector<TSource>(propertyName); var methodName = isDescending ? nameof(Queryable.OrderByDescending) : nameof(Queryable.OrderBy); var orderMethod = typeof(Queryable).GetMethods() .First(m => m.Name == methodName && m.GetParameters().Length == 2) .MakeGenericMethod(typeof(TSource), keySelector.ReturnType); return (IOrderedQueryable<TSource>)orderMethod.Invoke(null, new object[] { source, keySelector })!; } // 对应ThenBy的动态实现 public static IOrderedQueryable<TSource> ThenByDynamic<TSource>(this IOrderedQueryable<TSource> source, string propertyName, bool isDescending = false) where TSource : MyEntity { var keySelector = GetKeySelector<TSource>(propertyName); var methodName = isDescending ? nameof(Queryable.ThenByDescending) : nameof(Queryable.ThenBy); var orderMethod = typeof(Queryable).GetMethods() .First(m => m.Name == methodName && m.GetParameters().Length == 2) .MakeGenericMethod(typeof(TSource), keySelector.ReturnType); return (IOrderedQueryable<TSource>)orderMethod.Invoke(null, new object[] { source, keySelector })!; }
这种方式后续新增属性不需要修改任何代码,只要传入正确的属性名就能正常工作。
内容的提问来源于stack exchange,提问作者Mielipuoli
相关产品推荐
相关产品推荐

