使用EF构建企业级应用(二)
2012-04-07 16:49 谢中涞 阅读(2732) 评论(6) 编辑 收藏 举报本系列目录:
使用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这样的参数形式,那么我们如何动态的来构建这个查询表达式呢?那我们就在下一章节中来讨论下这个问题.
本着互相分享的精神,文章欢迎转载,但转载需要标明本文出处.除特殊声明外,本文章均首发于博客园.