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

  • 相关阅读:
    linux系统禁止root用户通过ssh登录及ssh的访问控制
    POJ 3670 , 3671 LIS
    hello world是怎样运行的?
    MFC框架中消失的WinMain()
    [置顶] android LBS的研究与分享(附PPT)
    POJ 3616 DP
    IMP 导入数据报错 OCI-21500 OCI-22275
    误删/tmp导致hadoop无法启停, jsp无法查看的解决方法
    java的文件操作类File
    C#的可空类型与不可空类型
  • 原文地址:https://www.cnblogs.com/sjqq/p/8635458.html
Copyright © 2011-2022 走看看