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

简介: ASP.NET MVC & EF 构建智能查询 一、智能查询的需求与设计 ASP.NET MVC & EF 构建智能查询 二、模型的设计与ModelBinder 上节说到我们已经将表单转化为了QueryModel 并且将查询条件按我们的设计存为了ConditionItem。

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

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

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

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

image

当然,这里的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")为例

构造的过程为:

image

构建形如 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

目录
相关文章
|
NoSQL Java Linux
《docker高级篇(大厂进阶):2.DockerFile解析》包括:是什么、DockerFile构建过程解析、DockerFile常用保留字指令、案例、小总结
《docker高级篇(大厂进阶):2.DockerFile解析》包括:是什么、DockerFile构建过程解析、DockerFile常用保留字指令、案例、小总结
660 76
|
自然语言处理 算法 Python
再谈递归下降解析器:构建一个简单的算术表达式解析器
本文介绍了递归下降解析器的原理与实现,重点讲解了如何使用Python构建一个简单的算术表达式解析器。通过定义文法、实现词法分析器和解析器类,最终实现了对基本算术表达式的解析与计算功能。
530 52
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
存储 人工智能 程序员
通义灵码AI程序员实战:从零构建Python记账本应用的开发全解析
本文通过开发Python记账本应用的真实案例,展示通义灵码AI程序员2.0的代码生成能力。从需求分析到功能实现、界面升级及测试覆盖,AI程序员展现了需求转化、技术选型、测试驱动和代码可维护性等核心价值。文中详细解析了如何使用Python标准库和tkinter库实现命令行及图形化界面,并生成单元测试用例,确保应用的稳定性和可维护性。尽管AI工具显著提升开发效率,但用户仍需具备编程基础以进行调试和优化。
1117 9
|
云安全 人工智能 安全
阿里云网络安全体系解析:如何构建数字时代的"安全盾牌"
在数字经济时代,阿里云作为亚太地区最大的云服务提供商,构建了行业领先的网络安全体系。本文解析其网络安全架构的三大核心维度:基础架构安全、核心技术防护和安全管理体系。通过技术创新与体系化防御,阿里云为企业数字化转型提供坚实的安全屏障,确保数据安全与业务连续性。案例显示,某金融客户借助阿里云成功拦截3200万次攻击,降低运维成本40%,响应时间缩短至8分钟。未来,阿里云将继续推进自适应安全架构,助力企业提升核心竞争力。
|
监控 前端开发 API
一款基于 .NET MVC 框架开发、功能全面的MES系统
一款基于 .NET MVC 框架开发、功能全面的MES系统
775 5
|
弹性计算 持续交付 API
构建高效后端服务:微服务架构的深度解析与实践
在当今快速发展的软件行业中,构建高效、可扩展且易于维护的后端服务是每个技术团队的追求。本文将深入探讨微服务架构的核心概念、设计原则及其在实际项目中的应用,通过具体案例分析,展示如何利用微服务架构解决传统单体应用面临的挑战,提升系统的灵活性和响应速度。我们将从微服务的拆分策略、通信机制、服务发现、配置管理、以及持续集成/持续部署(CI/CD)等方面进行全面剖析,旨在为读者提供一套实用的微服务实施指南。
|
监控 持续交付 数据库
构建高效的后端服务:微服务架构的深度解析
在现代软件开发中,微服务架构已成为提升系统可扩展性、灵活性和维护性的关键。本文深入探讨了微服务架构的核心概念、设计原则和最佳实践,通过案例分析展示了如何在实际项目中有效地实施微服务策略,以及面临的挑战和解决方案。文章旨在为开发者提供一套完整的指导框架,帮助他们构建出更加高效、稳定的后端服务。
|
存储 .NET
asp.net 生成、解析条形码和二维码
原文 asp.net 生成、解析条形码和二维码 一、条形码 一维码,俗称条形码,广泛的用于电子工业等行业。比如我们常见的书籍背面就会有条形码,通过扫描枪等设备扫描就可以获得书籍的ISBN(International standard book number,国际标准书号)。
1471 0
|
存储 .NET
asp.net 生成、解析条形码和二维码
一、条形码 一维码,俗称条形码,广泛的用于电子工业等行业。比如我们常见的书籍背面就会有条形码,通过扫描枪等设备扫描就可以获得书籍的ISBN(International standard book number,国际标准书号)。
1163 0

推荐镜像

更多
  • DNS