下面是 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);

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

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

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

调用 OrderBy

直接传入调用是不行的:

1
repository.OrderBy(keySelector);

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

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

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

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

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

c# 扩展方法 奇思妙用 高级篇 九:OrderBy(string propertyName, bool desc)的更多相关文章

  1. C# 扩展方法奇思妙用高级篇六:WinForm 控件选择器

    在Web开发中,jQuery提供了功能异常强大的$选择器来帮助我们获取页面上的对象.但在WinForm中,.Net似乎没有这样一个使用起来比较方便的选择器.好在我们有扩展方法,可以很方便的打造一个. ...

  2. c# 扩展方法奇思妙用基础篇九:Expression 扩展

    http://www.cnblogs.com/ldp615/archive/2011/09/15/expression-extension-methods.html .net 中创建 Expressi ...

  3. c# 扩展方法奇思妙用基础篇八:Distinct 扩展(转载)

    转载地址:http://www.cnblogs.com/ldp615/archive/2011/08/01/distinct-entension.html 刚看了篇文章 <Linq的Distin ...

  4. c# 扩展方法奇思妙用基础篇八:Distinct 扩展

    刚看了篇文章 <Linq的Distinct太不给力了>,文中给出了一个解决办法,略显复杂. 试想如果能写成下面的样子,是不是更简单优雅 var p1 = products.Distinct ...

  5. c# 扩展方法奇思妙用基础篇五:Dictionary<TKey, TValue> 扩展

    Dictionary<TKey, TValue>类是常用的一个基础类,但用起来有时确不是很方便.本文逐一讨论,并使用扩展方法解决. 向字典中添加键和值 添加键和值使用 Add 方法,但很多 ...

  6. c# 扩展方法奇思妙用

    # 扩展方法出来已久,介绍扩展方法的文章也很多,但都是笼统的.本人最近一直在思考扩展方法的应用,也悟出了一些,准备将这最近一段时间对扩展方法的思考,写成一个系列文章.每个文章只介绍一个应用方面,篇幅不 ...

  7. c# 扩展方法奇思妙用集锦

    本文转载:http://www.cnblogs.com/ldp615/archive/2009/08/07/1541404.html 其中本人觉得很经典的:c# 扩展方法奇思妙用基础篇五:Dictio ...

  8. EF OrderBy(string propertyname), OrderByDescending(string propertyname) 按属性排序,扩展方法

    public static class LinqExtensions { private static PropertyInfo GetPropertyInfo(Type objType, strin ...

  9. C# 扩展方法集

    语法注意点 可以使用扩展方法来扩展类或接口. 不能重写扩展方法. 扩展方法只能在非嵌套.非泛型静态类内部定义. 扩展方法必须定义在静态类中. 扩展方法的第一个参数的类型用于指定被扩展的类型,它限制该扩 ...

随机推荐

  1. EMQ学习---客户链接资源消耗

    Erlang进程消耗EMQ对客户端链接使用链接进程(emqtt_client)和session进程(emqtt_session)分开的策略. 当一个mqtt的客户端连接到EMQ的服务器上的时候,首先会 ...

  2. Android 使用Post方式提交数据

    在Android中,提供了标准Java接口HttpURLConnection和Apache接口HttpClient,为客户端HTTP编程提供了丰富的支持. 在HTTP通信中使用最多的就是GET和POS ...

  3. ArcGIS 10 安装程序及破解文件

    1.下载 ArcGIS 10 安装程序及破解文件 后面提供电驴的下载地址(可以使用迅雷.QQ旋风等下载工具下载),下载文件是一个光盘镜像文件:‍ArcGIS_Desktop10_122519.iso. ...

  4. (一)Mina源代码解析之总体架构

    Apache Mina Server 是一个网络通信应用框架.也就是说,它主要是对基于TCP/IP.UDP/IP协议栈的通信框架(当然,也能够提供JAVA 对象的序列化服务.虚拟机管道通信服务等).M ...

  5. Python 列表 list() 方法

    描述 Python 列表 list() 方法用于将可迭代对象(字符串.列表.元祖.字典)转换为列表. 注:元组与列表是非常类似的,区别在于元组的元素值不能修改,元组是放在括号中,列表是放于方括号中. ...

  6. C# Debug

    语法.IDE环境使用.Debug方法是学习一门语言的最少必须技能,本文总结C#中的最常用调试方法 一. 断点 如下图所示在欲插入断点的地方右键>断点>插入断点(或在行号左边点击)可在选中语 ...

  7. Linux下从视频提取音频的方法

    Linux下可以利用mencoder将视频里的音频提取出来.方法如下: 1.首先安装mencoder.对于Ubuntu来说,软件仓库里就有mencoder,可直接输入如下命令安装 sudo apt-g ...

  8. MySQL主从不一致情形与解决方法

    参考:https://blog.csdn.net/hardworking0323/article/details/81046408 https://blog.csdn.net/lijingkuan/a ...

  9. Redis(七):Jedis简介和集群

    Jedis简介 1.Jedis 是Redis 客户端工具jar2.使用非集群版示例代码 Jedis jedis = new Jedis("192.168.139.132", 637 ...

  10. maven optional可选依赖

    应用场景:projectA 依赖projectB,  projectB 依赖projectC时 <dependency> <groupId>com.itear.projectC ...