c# 扩展方法 奇思妙用 高级篇 九:OrderBy(string propertyName, bool desc)

摘要:
我们从Queryable类定义的OrderBy和OrderByDescending方法开始。它们都有一个类型为Expression<Func<TSource,TKey>>的keySelector参数。它可以通过强制类型转换来解决。编译和运行都可以:1存储OrderBy;但通过这种方式,它又变成了硬编码。或者将keySelector声明为dynamic:12dynamicSelector=Expression。Lambda;varorderedQueryable=可查询。OrderBy;好了,完成了!按属性名排序太常见,因此它被提取为扩展方法:OrderBy扩展方法对上述代码进行排序,扩展方法如下:1234567891011 public static classQueryableExtensions{public static IQueryable<T>OrderBy<T>{returnOrderBy;}publicstaticQueryable<T>OrderBy<T>{varparam=Expression.Parameter;varbody=Expression.Property;dynamicSelector=Expression.Lambda;returndesc?Queryable.OrderByDescending:Queryable.OOrderBy;}}注意,上面的代码可以毫无问题地执行,但效率不高。

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

免责声明:文章转载自《c# 扩展方法 奇思妙用 高级篇 九:OrderBy(string propertyName, bool desc)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇spring读取外部配置文件css 控制文字超出时显示省略号下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C#调用WPS将文档转换成pdf进行预览

vs启动项目可以生成wps实例 本地iis部署的站点却不行 原因是vs是管理员权限,而iis没有权限 解决方法 启动IIS,应用程序池-“选定的应用程序池”-高级设置-进程模型-标识:设置为管理员账号administrator     代码 1.安装WPS 2016 专业版 2.方法一:在项目中引用etapi.dll,wpsapi.dll,wpp...

VS2012_MVC4入门例子、代码视图分离办法、需要注意的坑爹问题等_被坑后不断更新此贴,要转载的话,请使用链接,不要转载内容

因为此贴会不断更新,所以,要转载的话,请使用链接,不要转载内容。 --------------------------------------------- 题外话: 用C#做网站,目前主要有两种模式:Asp.net网站,基于WebForm,和Mvc4 (mvc3、其他自定义框架就不谈了)。也就是做 xx.aspx页面,并且工具栏有一堆现成的.net服务器...

wchar_t,char,string,wstring等的总结

一、LPSTR LPCSTR LPTSTR LPCTSTR等 确定的类型: LPSTR = CHAR * = char *LPCSTR = const CHAR * = char * //c意为const  不确定类型(可变型): LPTSTR = LPWSTR = WCHAR * = wchar_t * //(Unicode编码) = LPSTR =...

关于ACCESS中TOP 功能的一个问题

我一直以为ACCESS的SQL语句与SQL Server差不多,当时也使用 Select top n在ACCESS测试过,都是成功的,但昨天突然发现ACCESS中有个问题:   如果在查询TOP语句的后面使用Order by,而且Order by字段中有重复值的话,那么这个TOP很可能会失效, 会返回所有记录 比如:  Select top 5 from ...

java 生成二维码

最近有点时间想学点东西,想做个简单的系统,现在登录的时候使用扫描二维码获取验证码登录,于是就有了下面的一些代码 首先要导入pom依赖 <dependency> <groupId>com.google.zxing</groupId> <artifactId>co...

易优CMS:arclist 文档列表

arclist 文档列表(配合arcpagelist标签可实现ajax瀑布流分页)  [基础用法] 名称:arclist 功能:获取系统主从表模型(如:文章、软件、图集、产品等)的一列文档,也称自由列表块标记。 语法: {eyou:arclist typeid='栏目ID' limit='0,10' flag='c' titlelen='30' infol...