c# 扩展方法 奇思妙用 高级篇 九:OrderBy(string propertyName, bool desc)
2012-01-15 18:41 鹤冲天 阅读(30257) 评论(20) 编辑 收藏 举报下面是 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); |
《c#扩展方法奇思妙用》系列文章已有 25 篇,欢迎阅读。
PS:简单编码,快乐生活!
-------------------
思想火花,照亮世界
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库