ASP.NET MVC & EF 构建智能查询 三、解析QueryModel

ASP.NET MVC & EF 构建智能查询 一、智能查询的需求与设计

ASP.NET MVC & EF 构建智能查询 二、模型的设计与ModelBinder

上节说到我们已经将表单转化为了QueryModel

并且将查询条件按我们的设计存为了ConditionItem。并且传递到了IQueryable.Where扩展方法中,对EF进行了查询:

当然,这里的Where是一个IQueryable的扩展方法,其中调用了将QueryModel转换为Expression表达式的类QueryableSearcher。

   1: public static class QueryableExtensions
   2: {
   3:     /// <summary>
   4:     /// zoujian add , 使IQueryable支持QueryModel
   5:     /// </summary>
   6:     /// <typeparam name="TEntity"></typeparam>
   7:     /// <param name="table">IQueryable的查询对象</param>
   8:     /// <param name="model">QueryModel对象</param>
   9:     /// <param name="prefix">使用前缀区分查询条件</param>
  10:     /// <returns></returns>
  11:     public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> table, QueryModel model, string prefix = "") where TEntity : class
  12:     {
  13:         Contract.Requires(table != null);
  14:         return Where<TEntity>(table, model.Items, prefix);
  15:     }
  16:  
  17:     private static IQueryable<TEntity> Where<TEntity>(IQueryable<TEntity> table, IEnumerable<ConditionItem> items, string prefix = "")
  18:     {
  19:         Contract.Requires(table != null);
  20:         IEnumerable<ConditionItem> filterItems =
  21:             string.IsNullOrWhiteSpace(prefix)
  22:                 ? items.Where(c => string.IsNullOrEmpty(c.Prefix))
  23:                 : items.Where(c => c.Prefix == prefix);
  24:         if (filterItems.Count() == 0) return table;
  25:         return new QueryableSearcher<TEntity>(table, filterItems).Search();
  26:     }
  27: }

这里面我们调用的QueryableSearcher其实就是我们将QueryModel转为Expression表达式并进行查询返回IQueryable的核心类。

db.Users.Where(c => c.Id < 10 && (c.Name == "chhlgy" || c.Name == "chsword")).ToList();

中的表达式

c => c.Id < 10 && (c.Name == "chhlgy" || c.Name == "chsword")为例

构造的过程为:

构建形如 c=>Body 的表达式

我们使用如下方法,也就是QueryableSearcher类的入口Search方法

   1: public IQueryable<T> Search()
   2: {
   3:     //构建 c=>Body中的c
   4:     ParameterExpression param = Expression.Parameter(typeof(T), "c");
   5:     //构建c=>Body中的Body
   6:     var body = GetExpressoinBody(param, Items);
   7:     //将二者拼为c=>Body
   8:     var expression = Expression.Lambda<Func<T, bool>>(body, param);
   9:     //传到Where中当做参数,类型为Expression<Func<T,bool>>
  10:     return Table.Where(expression);
  11: }

1.构建参数 c

在上文中使用

ParameterExpression param = Expression.Parameter(typeof(T), "c"); 来构建了参数c

2.构建 Body

就是我们前面的GetExpressionBody方法所生成的

   1: private Expression GetExpressoinBody(ParameterExpression param, IEnumerable<ConditionItem> items)
   2: {
   3:     var list = new List<Expression>();
   4:     //OrGroup为空的情况下,即为And组合
   5:     var andList = items.Where(c => string.IsNullOrEmpty(c.OrGroup));
   6:     //将And的子Expression以AndAlso拼接
   7:     if (andList.Count() != 0)
   8:     {
   9:         list.Add(GetGroupExpression(param, andList, Expression.AndAlso));
  10:     }
  11:     //其它的则为Or关系,不同Or组间以And分隔
  12:     var orGroupByList = items.Where(c => !string.IsNullOrEmpty(c.OrGroup)).GroupBy(c => c.OrGroup);
  13:     //拼接子Expression的Or关系
  14:     foreach (IGrouping<string, ConditionItem> group in orGroupByList)
  15:     {
  16:         if (group.Count() != 0)
  17:             list.Add(GetGroupExpression(param, group, Expression.OrElse));
  18:     }
  19:     //将这些Expression再以And相连
  20:     return list.Aggregate(Expression.AndAlso);
  21: }

3.构建分组的逻辑关系 And/OR

也就是根据Or或And来拼接不同的Expression的GetGroupExpression方法

   1: private Expression GetGroupExpression(ParameterExpression param, IEnumerable<ConditionItem> items, Func<Expression, Expression, Expression> func)
   2: {
   3:     //获取最小的判断表达式
   4:     var list = items.Select(item => GetExpression(param, item));
   5:     //再以逻辑运算符相连
   6:     return list.Aggregate(func);
   7: }

4.构建分组的单元 单一的表达式 c.User<10

这里要获取三部分,分别是左侧的属性,这里的属性可能是多级

右侧的常量,这里的常量可能要有类型转换的问题,因为Expression要求类型

中间的判断符号或其它方法如Contains

   1: private Expression GetExpression(ParameterExpression param, ConditionItem item)
   2: {
   3:     //属性表达式
   4:     LambdaExpression exp = GetPropertyLambdaExpression(item, param);
   5:     //如果有特殊类型处理,则进行处理,暂时不关注
   6:     foreach (var provider in TransformProviders)
   7:     {
   8:         if (provider.Match(item, exp.Body.Type))
   9:         {
  10:             return GetGroupExpression(param, provider.Transform(item, exp.Body.Type), Expression.AndAlso);
  11:         }
  12:     }
  13:     //常量表达式
  14:     var constant = ChangeTypeToExpression(item, exp.Body.Type);
  15:     //以判断符或方法连接
  16:     return ExpressionDict[item.Method](exp.Body, constant);
  17: }

5.获取左侧的属性及类型

   1: private LambdaExpression GetPropertyLambdaExpression(ConditionItem item, ParameterExpression param)
   2: {
   3:     //获取每级属性如c.Users.Proiles.UserId
   4:     var props = item.Field.Split('.');
   5:     Expression propertyAccess = param;
   6:     var typeOfProp = typeof(T);
   7:     int i = 0;
   8:     do
   9:     {
  10:         PropertyInfo property = typeOfProp.GetProperty(props[i]);
  11:         if (property == null) return null;
  12:         typeOfProp = property.PropertyType;
  13:         propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
  14:         i++;
  15:     } while (i < props.Length);
  16:  
  17:     return Expression.Lambda(propertyAccess, param);
  18: }

6.获取操作符或方法

这里只列举了QueryMethod枚举的操作方法

   1: private static readonly Dictionary<QueryMethod, Func<Expression, Expression, Expression>> ExpressionDict =
   2:             new Dictionary<QueryMethod, Func<Expression, Expression, Expression>>
   3:                 {
   4:                     {
   5:                         QueryMethod.Equal,
   6:                         (left, right) => { return Expression.Equal(left, right); }
   7:                         },
   8:                     {
   9:                         QueryMethod.GreaterThan,
  10:                         (left, right) => { return Expression.GreaterThan(left, right); }
  11:                         },
  12:                     {
  13:                         QueryMethod.GreaterThanOrEqual,
  14:                         (left, right) => { return Expression.GreaterThanOrEqual(left, right); }
  15:                         },
  16:                     {
  17:                         QueryMethod.LessThan,
  18:                         (left, right) => { return Expression.LessThan(left, right); }
  19:                         },
  20:                     {
  21:                         QueryMethod.LessThanOrEqual,
  22:                         (left, right) => { return Expression.LessThanOrEqual(left, right); }
  23:                         },
  24:                     {
  25:                         QueryMethod.Contains,
  26:                         (left, right) =>
  27:                             {
  28:                                 if (left.Type != typeof (string)) return null;
  29:                                 return Expression.Call(left, typeof (string).GetMethod("Contains"), right);
  30:                             }
  31:                         },
  32:                     {
  33:                         QueryMethod.StdIn,
  34:                         (left, right) =>
  35:                             {
  36:                                 if (!right.Type.IsArray) return null;
  37:                                 //调用Enumerable.Contains扩展方法
  38:                                 MethodCallExpression resultExp =
  39:                                     Expression.Call(
  40:                                         typeof (Enumerable),
  41:                                         "Contains",
  42:                                         new[] {left.Type},
  43:                                         right,
  44:                                         left);
  45:  
  46:                                 return resultExp;
  47:                             }
  48:                         },
  49:                     {
  50:                         QueryMethod.NotEqual,
  51:                         (left, right) => { return Expression.NotEqual(left, right); }
  52:                         },
  53:                     {
  54:                         QueryMethod.StartsWith,
  55:                         (left, right) =>
  56:                             {
  57:                                 if (left.Type != typeof (string)) return null;
  58:                                 return Expression.Call(left, typeof (string).GetMethod("StartsWith", new[] {typeof (string)}), right);
  59:  
  60:                             }
  61:                         },
  62:                     {
  63:                         QueryMethod.EndsWith,
  64:                         (left, right) =>
  65:                             {
  66:                                 if (left.Type != typeof (string)) return null;
  67:                                 return Expression.Call(left, typeof (string).GetMethod("EndsWith", new[] {typeof (string)}), right);
  68:                             }
  69:                         },
  70:                     {
  71:                         QueryMethod.DateTimeLessThanOrEqual,
  72:                         (left, right) => { return Expression.LessThanOrEqual(left, right); }
  73:                         }
  74:                 };

7.将Value中的值转为目标类型

   1: /// <summary>
   2: /// 类型转换,支持非空类型与可空类型之间的转换
   3: /// </summary>
   4: /// <param name="value"></param>
   5: /// <param name="conversionType"></param>
   6: /// <returns></returns>
   7: public static object ChangeType(object value, Type conversionType)
   8: {
   9:     if (value == null) return null;
  10:     return Convert.ChangeType(value, TypeUtil.GetUnNullableType(conversionType));
  11: }
  12:  
  13: /// <summary>
  14: /// 转换SearchItem中的Value的类型,为表达式树
  15: /// </summary>
  16: /// <param name="item"></param>
  17: /// <param name="conversionType">目标类型</param>
  18: public static Expression ChangeTypeToExpression(ConditionItem item, Type conversionType)
  19: {
  20:     if (item.Value == null) return Expression.Constant(item.Value, conversionType);
  21:     #region 数组
  22:     if (item.Method == QueryMethod.StdIn)
  23:     {
  24:         var arr = (item.Value as Array);
  25:         var expList = new List<Expression>();
  26:         //确保可用
  27:         if (arr != null)
  28:             for (var i = 0; i < arr.Length; i++)
  29:             {
  30:                 //构造数组的单元Constant
  31:                 var newValue = ChangeType(arr.GetValue(i), conversionType);
  32:                 expList.Add(Expression.Constant(newValue, conversionType));
  33:             }
  34:         //构造inType类型的数组表达式树,并为数组赋初值
  35:         return Expression.NewArrayInit(conversionType, expList);
  36:     }
  37:  
  38:     #endregion
  39:  
  40:     var elementType = TypeUtil.GetUnNullableType(conversionType);
  41:     var value = Convert.ChangeType(item.Value, elementType);
  42:     return Expression.Constant(value, conversionType);
  43: }

源代码下载:

http://efsearchmodel.codeplex.com/releases/view/63921

时间: 2024-10-12 20:17:16

ASP.NET MVC & EF 构建智能查询 三、解析QueryModel的相关文章

ASP.NET MVC &amp; EF 构建智能查询 一、智能查询的需求与设计

关于复用 在我们日常的开发过程中,代码的复用其实是很重要的一部分,ASP.NET MVC框架本身为我们提供了很多很好的复用机制,让我们能充分地利用它们来节省我们的Coding成本. 在简单的Coding中,我们可以通过构造方法来实现代码段的复用,在OOP编程中我们可以使用继承多态来进行类的复用,我们也可以使用设计模式来做类或对象间的代码设计的复用,随着程序的复杂我们就想构造出更佳的复用方式,可以向更高层次上抽象.   应用场景与目标 在信息管理系统中我们会开发大量的List页面,它们功能上通常是

ASP.NET MVC &amp; EF 构建智能查询 二、模型的设计与ModelBinder

在第一篇中,我讲解了我们要做智能查询的原因,以及基本的解决方案设计.从这篇开始我们开始讲解它的实现过程. 其实在写这一系列文章之初,我其实是想由底至上去讲解,但是我又整理了一遍代码才发现,其实如果不了解最表面的东西,也是不太好深入的. 所以我们的第二篇文章就来讲一下我们这个智能查询框架中最浅,但也是使用最频繁的部分,也就是Model. 首先我们的Entity  或者说数据库的结构如下 另外如下面代码,我们有一个用于传递name=value对,及查询谓词的model 1: public Actio

ASP.NET MVC &amp;amp; EF 构建智能查询 一、智“.NET研究”能查询的需求与设计

关于复用 在我们日常的开发过程中,代码的复用其实是很重要的一部分,ASP.NET MVC框架本身为我们提供了很多很好的复用机制,让我们能充分地利用它们来节省我们的Coding成本. 在简单的Coding中,我们可以通过构造方法来实现代码段的复用,在OOP编程中我们可以使用继承多态来进行类的复用,我们也可以使用设计模式来做类或对象间的代码设计的复用,随着程序的复杂我们就想构造出更佳的复用方式,可以向更高层次上抽象. 应用场景与目标 在信息管理系统中我们会开发大量的List页面,它们功能上通常是非常

ASP.NET MVC &amp;amp; EF 构建智能查询 二、模型的设计“.NET研究”与ModelBinder

在第一篇中,我讲解了我们要做智能查询的原因,以及基本的解决方案设计.从这篇开始我们开始讲解它的实现过程. 其实在写这一系列文章之初,我其实是想由底至上去讲解,但是我又整理了一遍代码才发现,其实如果不了解最表面的东西,也是不太好深入的. 所以我们的第二篇文章就来讲一下我们这个智能查询框架中最浅,但也是使用最频繁的部分,也就是Model. 首先我们的Entity  或者说数据库的结构如下: 另外如下面代码,我们有一个用于传递name=value对,及查询谓词的model: public Action

一起谈.NET技术,ASP.NET MVC &amp;amp; EF 构建智能查询 二、模型的设计与ModelBinder

在第一篇中,我讲解了我们要做智能查询的原因,以及基本的解决方案设计.从这篇开始我们开始讲解它的实现过程. 其实在写这一系列文章之初,我其实是想由底至上去讲解,但是我又整理了一遍代码才发现,其实如果不了解最表面的东西,也是不太好深入的. 所以我们的第二篇文章就来讲一下我们这个智能查询框架中最浅,但也是使用最频繁的部分,也就是Model. 首先我们的Entity  或者说数据库的结构如下: 另外如下面代码,我们有一个用于传递name=value对,及查询谓词的model: public Action

ASP.NET MVC+EF框架+EasyUI实现权限管理系列(16)-类库架构扩展以及DLL文件生成修改和用户的简单添加

原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(16)-类库架构扩展以及DLL文件生成修改和用户的简单添加 ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇)   (1):框架搭建    (2):数据库访问层的设计Demo    (3):面向接口编程   (4 ):业务逻辑层的封装    (5):前台Jquery easyUI实现    (6):EF上下文实例管理    (7):DBSession的封装   (8):DBSession线程内唯一     

ASP.NET MVC+EF框架+EasyUI实现权限管理系列(4)-业务逻辑层的封装

原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(4)-业务逻辑层的封装 ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇)   (1):框架搭建    (2):数据库访问层的设计Demo    (3):面向接口编程 前言:前面几篇博客我们基本已经介绍完了搭建整个项目和数据库访问层以及一些业务逻辑层的实现,当然了,我们的数据库访问层这样还是可以在进行封装的,但是我到这里就行了吧,项目也不大,不需要那么麻烦的,那么我们今天开始介绍我们需要介绍的内容,那就是我

ASP.NET MVC+EF框架+EasyUI实现权限管理系列(3)-面向接口的编程

原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(3)-面向接口的编程 ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇)  (1)框架搭建    (2):数据库访问层的设计Demo 前言:这篇博客在数据访问层的基础上面我们继续学习对这个Demo的开发,希望大家都好好理解一下这个Demo的架构,到最后我实现权限的时候我就简单的说一下搭建过程就OK了,因为这个Demo的思想就是按照权限的设计方式去设计的,下面我们开始介绍面向接口的编程思想,如果感觉好的话可以

ASP.NET MVC+EF框架+EasyUI实现权限管理系列之开篇

原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列之开篇 前言:博客又有一段时间没有更新了,心里感觉这段时间空空的,好像什么都没有学下,所以就想写博客,所以就有了这个系列,这里当然也要感谢大家了,因这个项目我已经上传了,得到了很多网友的评价,也有好多人发邮件给我说这个框架容易出现问题,不能访问,这也是支持我写这个系列的动力,我将这个项目写成一个系列,可能要很长时间吧,但是我肯定会一直坚持,如果我哪里写的不好欢迎大家指出我们共同学习,而且我理解的也不是很透彻,所以我想在写这样一遍