zoukankan      html  css  js  c++  java
  • c# 扩展方法 奇思妙用 高级篇 九:OrderBy(string propertyName, bool desc)

    下面是 Queryable 类 中最常用的两个排序的扩展方法:

    1
    2
        public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector);
        public static IOrderedQueryable<TSource> OrderByDescending<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector);

    算上另外两个复杂点的,一共是四个方法,都是强类型的。

    虽然强类型优点多多,但有些情况下确显得不够灵活。

    强类型的缺点

    比如 web 应用中有如下 Url:

    在代码中我们如何写出强类型的查询?

    1
    2
    3
    4
    IQueryable<Order> query = /**/;
    string propertyName = /*从请求中获取,OrderDate*/;
    bool desc = /*从请求中获取,true*/;
    var data = query.Where(/*TODO: 如何写*/).ToArray();

    单凭 Queryable 类 中定义的 OrderBy 和 OrderByDescending, 是不可能简单直接写出来的,除非硬编码。

    那有如此做到灵活呢?我们从 Queryable 类 定义的 OrderBy 和 OrderByDescending 方法下手,它们均有一个 Expression<Func<TSource, TKey>> 类型的 keySelector 参数。

    先来试下能不能动态构建一个 keySelector。

    动态构建 keySelector 参数

    此部分要求对表达式树有一定了解,可查看:http://msdn.microsoft.com/zh-cn/library/bb397951(v=VS.100).aspx

    代码则相当简单:

    1
    2
    3
    4
    5
    6
    var type = typeof(Order);
    var propertyName = "OrderDate";
    //
    var param = Expression.Parameter(type, type.Name);
    var body = Expression.Property(param, propertyName);
    var keySelector = Expression.Lambda(body, param);

    最后三行代码动态构造了一颗表达式树:

    image

    和我们使用 lambda 表达式写出的效果是完全一样的:

    image

    这步比较顺利,下面来看如何调用:

    调用 OrderBy

    直接传入调用是不行的:

    1
    repository.OrderBy(keySelector);

    因为前面构建的 keySelector 是 LambdaExpression 类型的,而 OrderBy 要求是 Expression<Func<Order, DateTime>> 。

    但实质上 keySelector 就是 OrderBy 要求的类型:

    image

    因为强类型,居然不认自家人了!

    可以通过强制类型转换来解决,编译运行都没问题:

    1
    repository.OrderBy((Expression<Func<Order, DateTime>>)keySelector);

    但这样一来,又成了硬编码。

    我们期望灵活,解决方法有很多种,这里只介绍最简单的一种,借助 .net 4 中 dynamic

    1
    var orderedQueryable = Queryable.OrderBy(repository, (dynamic)keySelector);

    因为扩展方法是不能被动态调用的(Extension methods cannot be dynamically dispatched),所以写成上面样子。

    或将 keySelector 声明为 dynamic

    1
    2
    dynamic keySelector = Expression.Lambda(body, param);
    var orderedQueryable = Queryable.OrderBy(repository, keySelector);

    OK,搞定!根据属性名排序太常用了,遂提取成了扩展方法:

     

     

    OrderBy 扩展方法

    将上面代码整理下,扩展方法就出来了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public static class QueryableExtensions {
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> queryable, string propertyName) {
            return OrderBy(queryable, propertyName, false);
        }
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> queryable, string propertyName, bool desc) {
            var param = Expression.Parameter(typeof(T));
            var body = Expression.Property(param, propertyName);
            dynamic keySelector = Expression.Lambda(body, param);
            return desc ? Queryable.OrderByDescending(queryable, keySelector) : Queryable.OrderBy(queryable, keySelector);
        }
    }

    注意,上面代码执行没问题,但效率不好。因为每次都要动态生成表达式树,另外动态调用也会造成一定性能损失。

    想提高效率的话,可把动态生成的表达式树缓存起来,参考如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public static class QueryableExtensions {
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> queryable, string propertyName) {
            return QueryableHelper<T>.OrderBy(queryable, propertyName, false);
        }
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> queryable, string propertyName, bool desc) {
            return QueryableHelper<T>.OrderBy(queryable, propertyName, desc);
        }
        static class QueryableHelper<T> {
            private static Dictionary<string, LambdaExpression> cache = new Dictionary<string, LambdaExpression>();
            public static IQueryable<T> OrderBy(IQueryable<T> queryable, string propertyName, bool desc) {
                dynamic keySelector = GetLambdaExpression(propertyName);
                return desc ? Queryable.OrderByDescending(queryable, keySelector) : Queryable.OrderBy(queryable, keySelector);
            }
            private static LambdaExpression GetLambdaExpression(string propertyName) {
                if (cache.ContainsKey(propertyName)) return cache[propertyName];
                var param = Expression.Parameter(typeof(T));
                var body = Expression.Property(param, propertyName);
                var keySelector = Expression.Lambda(body, param);
                cache[propertyName] = keySelector;
                return keySelector;
            }
        }
    }

    这里并发不是多大问题,如若考虑,可使用 ConcurrentDictionary<TKey, TValue> 类

    使用

    很方便的:

    1
    2
    var data1 = productRepository.OrderBy("Name");
    var data2 = orderRepository.OrderBy("OrderDate", true);

    http://www.cnblogs.com/ldp615/archive/2012/01/15/orderby-extensions.html

  • 相关阅读:
    springboot文件上传: 单个文件上传 和 多个文件上传
    Eclipse:很不错的插件-devStyle,将你的eclipse变成idea风格
    springboot项目搭建:结构和入门程序
    POJ 3169 Layout 差分约束系统
    POJ 3723 Conscription 最小生成树
    POJ 3255 Roadblocks 次短路
    UVA 11367 Full Tank? 最短路
    UVA 10269 Adventure of Super Mario 最短路
    UVA 10603 Fill 最短路
    POJ 2431 Expedition 优先队列
  • 原文地址:https://www.cnblogs.com/sjqq/p/8635458.html
Copyright © 2011-2022 走看看