【问题标题】:Dynamically ordering by string按字符串动态排序
【发布时间】:2019-06-09 13:21:12
【问题描述】:

我在 IQueryable 上创建了一个扩展,因为我想先按可空日期时间排序,然后仅使用属性字符串(即“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 的记录排在第一位,但事实并非如此。

有谁知道我做错了什么?

【问题讨论】:

  • 您的代码构造表达式 OrderBy(p =&gt; p.activeTo == null) 和 true 位于 false 之后,因为 false 通常由 0 表示,true 由 1 表示。因此,您的表达式首先收集与null,然后所有值都为 null。但是,您的方法不应该生成像OrderBy(p =&gt; p.activeTo) 这样的表达式,这似乎更适合像 Sort 这样的名称吗?
  • @ckuri 是的,你说得对,我正在考虑创建另一个扩展程序,它可以按 null 排序,但我不确定该怎么做
  • 我看到你实际上是在生成OrderBy(p =&gt; p.activeTo == null).ThenBy(p =&gt; activeTo == null)。为什么?只需 OrderBy 就足够了。
  • @ckuri 我的错,这是一个错误。我想实际做 OrderBy(p => p.activeTo == null).ThenBy(p => activeTo)

标签: c# linq sorting


【解决方案1】:

从 cmets 看来,目标似乎是动态生成一个表达式,将 null 值排在前面。

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

  • 它将null 值排序到前面,因为布尔排序顺序是false、true(因为它们的序数值分别是0 和1)。因此,与null 的比较首先收集false 案例,然后是true 案例。
  • ThenBy 重复了OrderBy,但实际上是为了发出ThenBy(p =&gt; p.ActiveTo)。

第一个问题可以通过使用Expression.NotEqual 而不是Expression.Equal 来解决p =&gt; 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 =&gt; p.activeTo != null).ThenBy(p =&gt; p.activeTo).

备注:需要注意的是,通常OrderBy(p =&gt; p.activeTo) 已经将空值排在前面,因为这是字符串、可空值等的默认排序顺序。但是,此行为可能会被特定类型覆盖并取决于查询源。因此,我像 OP 一样离开了它。

【讨论】:

  • 产生的表达式并不是我想要实现的。相反,我想先获取所有为空的记录,然后按实际值对剩余的记录进行排序。因此我改变了:Expression.NotEqual(prop, Expression.Convert(target, prop.Type)); 到 Expression.Equa(prop, Expression.Convert(target, prop.Type)); 但空值仍然是我的结果中的最后一个
  • NotEqual 是将 null 值排序到前面的关键点,Equal 就像我解释的那样将它们排序到最后。
  • 使用NotEqual不起作用,这是我认为错误的地方所以我决定将其更改为Equal
猜你喜欢
  • 2014-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多