本系列目录:
使用EF构建企业级应用(一):主要讲数据库访问基类IRepository及Repository 的实现
使用EF构建企业级应用(二):主要讲动态排序扩展的实现
使用EF构建企业级应用(三):主要讲灵活的构建查询条件表达式Expression<Func<TEntity,bool>>.
使用EF构建企业级应用(四):主要讲下在MVC环境中前端开发中如何邮箱的使用,及一个实例源码包
动态排序扩展
在上一节(使用EF构建企业级应用(一) ) 中,我们实现了数据库基本操作的CURD的定义,如果你直接复制这个代码到VS中编译,奇怪的问题就出现了,可能会出好几个错误,错误发生在类似这样的代码上”IQueryable<TEntity> query = Get(expression).OrderBy(orderPropertyName, isAscending);”,大致的错误可能是,

这是为啥呢? 该不会是楼主忽悠吧,这个自然不会,且听如下分解.
我们常使用的排序可能是如下样子:var temp=lst.OrderBy(t=>t.Code).ToList();因为后端我们并不清楚在数据库查询的时候是对什么字段排序,那么我们如何动态来构建这个类似于OrderBy中的(t=>t.Code)表达式呢?
在定义中”public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector) ”,
/// <summary>
/// 动态排序
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="propertyName">按T类型中排序属性名</param>
/// <param name="isAscending">是否是升序排序</param>
/// <returns></returns>
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, bool isAscending) where T : class
{
var type = typeof(T);
Expression resultExp = null;
var property = type.GetProperty(propertyName);
if (property == null)
throw new ArgumentException("propertyName", "不存在");
var param = Expression.Parameter(type, "p");
Expression propertyAccessExpression = Expression.MakeMemberAccess(param, property);
var orderByExpression = Expression.Lambda(propertyAccessExpression, param);
var methodName = isAscending ? "OrderBy" : "OrderByDescending";
resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { type, property.PropertyType },
source.Expression, Expression.Quote(orderByExpression));
return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(resultExp);
}
上面方法终于解决动态排序的问题,直到有一天,业务上在查询产品信息的时候,需要按照产品大类名称排序,我们很自然的写成了var lst=lst.OrderBy(“ProductCategory.Name”,true)这样的调用,结果却出现了类似于
这样的错误,大致意思就是当前属性在指定的类型中不存在,很显然这样的写法只支持按实体简单的属性排序,没有办法做关联排序,为让大家更明白一点,把产品和产品大类定义描述一下
/// <summary>
/// 产品信息
/// </summary>
public class Product
{
public Guid Id{get;set;}
public string Name{get;set;}
//产品大类
public ProductCategory ProductCategory{get;set;}
}
public class ProductCategory
{
public Guid Id{get;set;}
public string Name{get;set;}
}
想必大家明白了问题的所在,我们是需要构建一个按 Product下面的ProductCategory 对象中的 Name字段排序,在有了上面的经验之后,经过不断的goodle+单步调试,我们对排序扩展有了如下的改进
var methodName = isAscending ? "OrderBy" : "OrderByDescending";
string[] props = property.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
object result = typeof(Queryable).GetMethods().Single(method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
下面贴一个完整版的排序解决方案,包含一个对IEnumerable<T>的排序扩展
/// <summary>
/// 排序类型
/// </summary>
public enum EOrderType
{
OrderBy = 0,
OrderByDescending = 1,
ThenBy = 2,
ThenByDescending = 3
}
/// <summary>
/// 排序扩展
/// </summary>
public static class OrderEx
{
/// <summary>
/// 动态排序
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="propertyName"></param>
/// <param name="isAscending"></param>
/// <returns></returns>
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, bool isAscending) where T : class
{
//有关联属性
if (propertyName.IndexOf('.') > 0)
{
if (isAscending)
return source.OrderBy(propertyName);
else
return source.OrderByDescending(propertyName);
}
//简单属性
else
{
var type = typeof(T);
Expression resultExp = null;
var property = type.GetProperty(propertyName);
if (property == null)
throw new ArgumentException("propertyName", "不存在");
var param = Expression.Parameter(type, "p");
Expression propertyAccessExpression = Expression.MakeMemberAccess(param, property);
var orderByExpression = Expression.Lambda(propertyAccessExpression, param);
var methodName = isAscending ? "OrderBy" : "OrderByDescending";
resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { type, property.PropertyType },
source.Expression, Expression.Quote(orderByExpression));
return source.Provider.CreateQuery<T>(resultExp);
}
}
/// <summary>
/// 升序排序
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="property"></param>
/// <returns></returns>
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, EOrderType.OrderBy);
}
/// <summary>
/// 降序排序
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="property"></param>
/// <returns></returns>
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, EOrderType.OrderByDescending);
}
/// <summary>
/// ThenBy
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="property"></param>
/// <returns></returns>
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, EOrderType.ThenBy);
}
/// <summary>
/// ThenByDescending
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="property"></param>
/// <returns></returns>
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, EOrderType.ThenByDescending);
}
/// <summary>
/// 应用排序
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="property"></param>
/// <param name="methodName"></param>
/// <returns></returns>
public static IOrderedQueryable<T> ApplyOrder<T>(this IQueryable<T> source, string property, EOrderType orderType)
{
var methodName = orderType.ToString();
string[] props = property.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
object result = typeof(Queryable).GetMethods().Single(method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
/// <summary>
/// 动态排序
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="propertyName"></param>
/// <param name="isAscSort"></param>
/// <returns></returns>
public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> source, string propertyName, bool isAscSort) where T : class
{
return ApplyOrder(source, propertyName, isAscSort ? EOrderType.OrderBy : EOrderType.OrderByDescending);
}
/// <summary>
/// 处理排序
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="property"></param>
/// <param name="methodName"></param>
/// <returns></returns>
public static IOrderedEnumerable<T> ApplyOrder<T>(IEnumerable<T> source, string property, EOrderType orderType)
{
var methodName = orderType.ToString();
string[] props = property.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
var orderMethod = typeof(Enumerable).GetMethods().Single(method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2);
object result = orderMethod
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda.Compile() });
return (IOrderedEnumerable<T>)result;
}
}
在第一篇文章中,细心的同学们可能会发现,我们在查询的时候,查询条件参数写成了Expression<Func<TEntity, bool>> expression这样的参数形式,那么我们如何动态的来构建这个查询表达式呢?那我们就在下一章节中来讨论下这个问题.
