通过字符串动态排序

时间:2019-06-09 13:21:12

标签: c# linq sorting

我已经在IQueryable上创建了扩展名,因为我想先按可为null的日期时间排序,然后仅使用属性字符串(即“ activeTo”)按日期时间本身进行排序。我创建了以下代码:

public static IQueryable<T> Sort<T>(this IQueryable<T> source, string sortBy)
    {
        //create the expression tree that represents the generic parameter to the predicate
        var param = Expression.Parameter(typeof(T), "p");

        //create an expression tree that represents the expression p=>p.SortField.HasValue 
        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);

        string method = "OrderBy";
        Type[] types = new Type[] { source.ElementType, exp.Body.Type };
        var orderByCallExpression = Expression.Call(typeof(Queryable), method, types, source.Expression, exp);

        //now do the ThenBy bit,sending in the above expression to the Expression.Call
        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");

然后给出以下响应:

 {
      "title": "test 5",
      "activeFrom": "2019-06-08T21:26:50.2833333",
      "activeTo": "2019-06-08T21:26:50.2833333",
 },
 {
      "title": "test 2",
      "activeFrom": "2019-06-08T21:28:45.65",
      "activeTo": null,
 }

我希望activeTo为null的记录排在第一位,

有人知道我在做什么错吗?

1 个答案:

答案 0 :(得分:1)

从注释中看,目标似乎是动态生成一个将null值排在最前面的表达式。

当前代码产生以下表达式OrderBy(p => p.activeTo == null).ThenBy(p => p.activeTo == null)。这有两个缺陷:

  • 由于布尔排序顺序为nullfalse(它们的序数分别为0和1),因此将true值排在最前面。因此,与null的比较首先收集false个案例,然后收集true个案例。
  • ThenBy重复OrderBy,但实际上是要发出ThenBy(p => p.ActiveTo)

第一个可以通过使用Expression.NotEqual而不是Expression.Equal的{​​{1}}或通过使用p => p != p.activeTo而不是OrderByDescending来解决。 总的代码应该是:

OrderBy

这将产生以下表达式: public static IQueryable<T> Sort<T>(IQueryable<T> source, string sortBy) { //create the expression tree that represents the generic parameter to the predicate var param = Expression.Parameter(typeof(T), "p"); //create an expression tree that represents the expression p=>p.SortField.HasValue var prop = Expression.Property(param, sortBy); var target = Expression.Constant(null, prop.Type); // NotEqual, to sort nulls before not-nulls var bin = Expression.NotEqual(prop, Expression.Convert(target, prop.Type)); var exp = Expression.Lambda(bin, param); // OrderBy with the null comparison expression string method = nameof(Queryable.OrderBy); Type[] types = new Type[] { source.ElementType, exp.Body.Type }; var orderByCallExpression = Expression.Call(typeof(Queryable), method, types, source.Expression, exp); // ThenBy with the property expression exp = Expression.Lambda(prop, param); types = new Type[] { source.ElementType, exp.Body.Type }; method = nameof(Queryable.ThenBy); var ThenByCallExpression = Expression.Call(typeof(Queryable), method, types, orderByCallExpression, exp); return source.Provider.CreateQuery<T>(ThenByCallExpression); }

备注:应注意,通常OrderBy(p => p.activeTo != null).ThenBy(p => p.activeTo)已经将空值排序在最前面,因为这是字符串,可空值等的默认排序顺序。但是,此行为可能会被特定的类型和查询源所覆盖。因此,我像OP一样保留了它。