【问题标题】:OrderBy expression tree with dynamic field name, prioritising non-null values具有动态字段名称的 OrderBy 表达式树,优先考虑非空值
【发布时间】:2021-11-04 20:57:55
【问题描述】:

我正在编写一个扩展方法,它将属性名称作为字符串并构建一个表达式树以按该列排序。它需要支持 Linq-to-Entities,因为我希望在 SQL 中完成 order-by 操作。该方法目前按预期工作,但是我现在想优先考虑非空值,即无论顺序方向如何,始终将空值留在集合末尾。

这是我目前的方法:

public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string orderBy, bool ascending) {
    Type type = typeof(T);
    ParameterExpression parameter = Expression.Parameter(type, "p");
    PropertyInfo property = type.GetProperty(orderBy);
    if (property == null) {
        throw new ArgumentException("The given value did not match any available properties.", nameof(orderBy));
    }

    // Get the property accessor p.SortColumn
    Expression propertyAccess = Expression.MakeMemberAccess(parameter, property);

    // Get the lambda expression p => p.SortColumn
    LambdaExpression orderByExp = Expression.Lambda(propertyAccess, parameter);

    // Call the OrderBy(Descending) method with the above lambda expression
    MethodCallExpression resultExp = Expression.Call(typeof(Queryable),
        ascending ? "OrderBy" : "OrderByDescending", new[] { type, property.PropertyType },
        source.Expression, Expression.Quote(orderByExp));

    return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(resultExp);
}

如果我不需要支持 Linq-to-Entities,我会这样写:

source.OrderBy(x => property.GetValue(x) == null).ThenBy/Descending(x => property.GetValue(x));

但是,Expression.Call()... 语法让我很困惑,因为我不完全了解幕后发生的事情。

如何将OrderBy(value == null) 操作添加到表达式树?

【问题讨论】:

    标签: c# linq-to-entities expression-trees


    【解决方案1】:

    先按等于null排序

    var result = list.OrderBy(x => x.Name == null).ThenBy(x => x.Name)
    

    示例

    public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, bool ascending)
    {
       var propertyInfo = typeof(T).GetProperty(propertyName) ?? throw new ArgumentException("The given value did not match any available properties.", nameof(propertyName));
    
       var parameterExpression = Expression.Parameter(typeof(T), "p");
       var propertyAccess = Expression.MakeMemberAccess(parameterExpression, propertyInfo);
    
       var orderByNullLambda = Expression.Lambda(
          Expression.Equal(propertyAccess, Expression.Constant(null)),
          parameterExpression);
    
       var resultExp = Expression.Call(
          typeof(Queryable),
          ascending ? "OrderBy" : "OrderByDescending",
          new[] {typeof(T), typeof(bool)},
          source.Expression,
          Expression.Quote(orderByNullLambda));
    
       var orderByPropertyLambda = Expression.Lambda(
          propertyAccess, 
          parameterExpression);
    
       var resultExp2 = Expression.Call(
          typeof(Queryable),
          ascending ? "ThenBy" : "ThenByDescending",
          new[] {typeof(T), propertyInfo.PropertyType},
          resultExp,
          Expression.Quote(orderByPropertyLambda));
    
       return (IOrderedQueryable<T>) source.Provider.CreateQuery<T>(resultExp2);
    }
    

    用法

    var list = new List<Bob>()
    {
       new() {Name = "Bsd"}, 
       new() {Name = null}, 
       new() {Name = "asd"}
    };
    
    var result = list.AsQueryable().OrderBy("Name", true).ToList();
    
    foreach (var item in result)
       Console.WriteLine(item.Name ?? "Null");
    

    结果

    asd
    Bsd
    Null
    

    【讨论】:

      猜你喜欢
      • 2015-08-27
      • 2016-04-13
      • 2021-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-15
      • 1970-01-01
      相关资源
      最近更新 更多