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

最新文章

  1. ILMerge合并多个DLL
  2. 【解决方法】安装Win7和linux双系统后,linux报错“无法分配所提交的分区 not enough free space on disks”问题,以及win7无法启动“BootMGR image is corrupt....”问题
  3. 常用的sql数据库语句
  4. 【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心
  5. 怎么捕获和记录SQL Server中发生的死锁
  6. sql随机
  7. OpenCV点滴2
  8. Java中的流程控制
  9. nginx启用status状态页
  10. 关于Java多线程的一些常考知识点
  11. 如何在Rails6内通过Webpacker使用JavaScript; flatpicker日期时间组件选择器
  12. PhoenixFD插件流体模拟——UI布局【Output】详解
  13. bzoj4540 序列 (单调栈+莫队+rmq)
  14. Codeforces 1138 - A/B/C/D/E - (Undone)
  15. ADC采样对MIC的干扰
  16. PAT 乙级 1072 开学寄语(20 分)
  17. ElasticSearch 2 (29) - 信息聚合系列之测试驱动
  18. 数学之美——HMM模型(一)介绍
  19. C#用openfiledialog文件和savefileDialog打开和保存文件
  20. [svc]java初步

热门文章

  1. Android 自己定义View (四) 视频音量调控
  2. 关于RDS备份文件使用wget下载提示403 Forbidden的情况
  3. Swift2.0-异常处理(Exception handler)
  4. TFS创建登录用户并连接TFS
  5. 随机用户id号,随机密码用户名
  6. OpenCV2马拉松第24圈——轮廓提取
  7. 玩转Linux文件描述符和重定向
  8. 【Android】15.3 Notification基础知识
  9. swift 继承和构造器
  10. windows phone 应用提交商店失败总结