基于字符串成员名扩展IQueryable.OrderBy时的无效输入处理
当然可行!其实思路很简单——当参数无效时,我们只需要返回一个不改变原序列顺序的排序表达式就行,本质上就是让排序操作变成一个“空操作”。而且必须这么做,否则后续如果链式调用ThenBy这类方法会直接报错(因为ThenBy仅支持IOrderedQueryable类型)。
具体实现思路
当检测到无效参数(比如传入的属性名不存在,导致pi为null)时,我们构造一个所有元素返回相同值的键选择器表达式。因为当所有元素的排序键完全相同时,排序算法会保留原序列的顺序(稳定排序特性),这样就等价于“没有执行排序”。同时,这个表达式会生成合法的IOrderedQueryable,确保后续链式调用不受影响。
修改后的完整代码
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string orderBy) { if (!string.IsNullOrWhiteSpace(orderBy)) { var resultExp = CreateExpression(source, "OrderBy", orderBy); return source.Provider.CreateQuery<T>(resultExp); } else { return source; } } private static MethodCallExpression CreateExpression<T>(IQueryable<T> source, string methodName, string orderBy) { if (methodName != "OrderBy" && methodName != "OrderByDescending" && methodName != "ThenBy" && methodName != "ThenByDescending") methodName = "OrderBy"; if (!string.IsNullOrWhiteSpace(orderBy)) { PropertyInfo pi = typeof(T).GetProperty(orderBy); if (pi != null) { var parameter = Expression.Parameter(typeof(T), "p"); MemberExpression me = Expression.MakeMemberAccess(parameter, pi); var orderByExp = Expression.Lambda(me, parameter); return Expression.Call(typeof(Queryable), methodName, new Type[] { typeof(T), pi.PropertyType }, source.Expression, Expression.Quote(orderByExp)); } } // 参数无效时,构造不改变原顺序的排序表达式 var parameter = Expression.Parameter(typeof(T), "p"); // 用object类型的null作为统一排序键,兼容所有类型 var keySelector = Expression.Lambda<Func<T, object>>( Expression.Constant(null, typeof(object)), parameter ); return Expression.Call( typeof(Queryable), methodName, new Type[] { typeof(T), typeof(object) }, source.Expression, Expression.Quote(keySelector) ); }
额外说明
- 如果你担心值类型的空引用问题,可以把键选择器改成返回
T的默认值并转换为object:var keySelector = Expression.Lambda<Func<T, object>>( Expression.Convert(Expression.Default(typeof(T)), typeof(object)), parameter ); - 这个方案同时兼容
OrderByDescending、ThenBy、ThenByDescending所有方法,因为不管是升序还是降序,当所有键相同时,顺序都不会改变。
内容的提问来源于stack exchange,提问作者crazysnake
相关产品推荐
相关产品推荐

