IQueryable动态字符串排序:可空DateTime排序异常排查
问题分析与解决方案
你的问题出在布尔值的排序规则上:.NET中对bool类型进行升序排序时,false(对应数值0)会排在true(对应数值1)前面。
你当前的代码里,第一个排序逻辑是p => p.activeTo == null,当记录的activeTo为null时,这个表达式返回true;非null时返回false。而你用的是OrderBy(升序),这就导致false的记录(非null)被排在了前面,和你预期的null记录优先完全相反。
两种修正方案
方案1:将第一个排序改为降序
直接把第一次调用的OrderBy改成OrderByDescending,这样true(null记录)会被优先排列:
public static IQueryable<T> Sort<T>(this IQueryable<T> source, string sortBy) { var param = Expression.Parameter(typeof(T), "p"); var prop = Expression.Property(param, sortBy); var target = Expression.Constant(null, prop.Type); var bin = Expression.Equal(prop, Expression.Convert(target, prop.Type)); var exp = Expression.Lambda(bin, param); // 这里改成OrderByDescending string method = "OrderByDescending"; Type[] types = new Type[] { source.ElementType, exp.Body.Type }; var orderByCallExpression = Expression.Call(typeof(Queryable), method, types, source.Expression, exp); // ThenBy逻辑保持不变 exp = Expression.Lambda(prop, param); types = new Type[] { source.ElementType, exp.Body.Type }; method = "ThenBy"; var ThenByCallExpression = Expression.Call(typeof(Queryable), method, types, orderByCallExpression, exp); return source.Provider.CreateQuery<T>(ThenByCallExpression); }
方案2:改用HasValue属性判断(更直观)
可空类型自带HasValue属性,我们可以直接判断这个属性,然后用升序排序——因为null记录的HasValue是false,升序时false会排在true前面,刚好符合需求:
public static IQueryable<T> Sort<T>(this IQueryable<T> source, string sortBy) { var param = Expression.Parameter(typeof(T), "p"); var prop = Expression.Property(param, sortBy); // 直接获取可空类型的HasValue属性 var hasValueProp = Expression.Property(prop, "HasValue"); var exp = Expression.Lambda(hasValueProp, param); string method = "OrderBy"; Type[] types = new Type[] { source.ElementType, exp.Body.Type }; var orderByCallExpression = Expression.Call(typeof(Queryable), method, types, source.Expression, exp); // ThenBy逻辑保持不变 exp = Expression.Lambda(prop, param); types = new Type[] { source.ElementType, exp.Body.Type }; method = "ThenBy"; var ThenByCallExpression = Expression.Call(typeof(Queryable), method, types, orderByCallExpression, exp); return source.Provider.CreateQuery<T>(ThenByCallExpression); }
验证效果
修正后调用query.Sort("activeTo"),你会得到预期的结果:activeTo为null的记录排在首位,之后的记录按activeTo的DateTime值升序排列。
内容的提问来源于stack exchange,提问作者KTOV
相关产品推荐
相关产品推荐

