概要
在后端开发中,我们常碰到这样的需求,用户需要对数据进行排序和分页,但是基于哪个栏位排序不确定,需要根据用户在页面上的操作动态获取,并且可能有一个或多个栏位参与排序。
对于此类需求的一般做法是,前端根据用户的输入生成一个对应的URL,包括分页数据和排序数据。例如: https://xxxx/students?pagestart=1&pagelength=10&orderby=Department desc,Name
对于后端,通过URL地址,获取分页和排序信息,需要使用EF的排序。我们想到的肯定是IQueryable的扩展方法OrderBy,但是OrderBy函数的参数是一个委托,我们并不能直接把解析URL获得的排序栏位的名称传入该方法。需要动态构建该委托。
基于上述需求,显然使用表达式目录树构建排序的表达式时最佳选择。
设计和实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public static class IQueryableExtension {
public static IQueryable<T> Sorting<T> (this IQueryable<T> source, string sortExpression, string sortDirection = "ASC", bool isThenBy = false) {
var query = DataSort<T> (source, sortExpression, sortDirection, isThenBy);
return query;
}
private static IQueryable<T> DataSort<T> (IQueryable<T> source, string sortExpression, string sortDirection = "ASC", bool isThenBy = false) {
string sortingDir = !isThenBy ? "OrderBy" : "ThenBy";
if (sortDirection.ToUpper ().Trim () == "DESC") {
sortingDir = !isThenBy ? "OrderByDescending" : "ThenByDescending";
}
ParameterExpression oParameter = Expression.Parameter (typeof (T), "o");
var property = typeof(T).GetProperty (sortExpression);
var memberExpression = Expression.Property (oParameter, property);
var orderExpression = Expression.Lambda (memberExpression, new ParameterExpression[] { oParameter });
var resultExpression = Expression.Call(
typeof(Queryable),
sortingDir,
new Type[] {source.ElementType, property.PropertyType},
new Expression[] { source.Expression, Expression.Quote(orderExpression)}
);
return source.Provider.CreateQuery<T>(resultExpression) as IQueryable<T>;
}
}
- 对于多列排序,第一次要使用OrderBy,后面的排序需要ThenBy, 降序排列同理。
- 构建排序的表达式 o=>o.FirstName
- 调用排序方法:
- C# 的Expression.Call有多个重载方法,排序方法称为IQueryable的扩展方法,扩展方法本质上还是静态方法,又要支持泛型。所以选用Call(Type, String, Type[], Expression[])重载方法,即支持泛型有支持静态方法调用。
- 泛型排序方法有两个泛型类型要指定,一个是泛型的具体类型,第二个是传入列名对应的类型。
- 在调用扩展方法时候,扩展方法的第一个参数是可省的,相当于哪个对象调用该方法,该对象就对应该参数。但是通过表达式目录树的方式调用该方法,第一个参数是不可以省略的。因此,要指定为source.Expression,source是具体调用该扩展方法的对象,要通过表达式的形式体现,因此为source.Expression。第二个参数是之前第二步构建好的排序表达式。