ASP.NET MVC:Expression Trees 作为参数简化查询
ASP.NET MVC 引入了 ModelBinder 技术,让我们可以在 Action 中以强类型参数的形式接收 Request 中的数据,极大的方便了我们的编程,提高了生产力。在查询 Action 中,我们可以将 Expression Trees 用作参数,通过自定义的 ModelBinder 动态自动构建查询表达式树,进一步发挥 MVC 的威力,简化编码工作。
先给出本文中使用的 Model:
1 |
public class Employee {
|
MVC 查询和存在的不足
下面是一个查询 Employee 的 Action,在 MVC 项目中经常可以见到:
1 |
public ActionResult Index(string firstName, string lastName, DateTime? birthday, bool? sex) {
|
得益于 MVC 的绑定技术,我们可以简单通过 Action 的参数来获取请求的值,很少再使用 Request["XXXX"] 的方式。
仔细观察,会发现上面这个 Action 中充斥着大量 if 判断,以致代码行数比较多,不是特别清晰。可以借助本人《c# 扩展方法奇思妙用基础篇 六:WhereIf 扩展》一文中的扩展方法予以简化:
1 |
public ActionResult Index2(string firstName, string lastName, DateTime? birthday, bool? sex) {
|
代码相清晰了许多,我之前的几个 MVC 项目中也是这样处理的。
但时间一长,我逐步也发现了这种方式一些不足之处:
- 首先,网站中有很多类似的查询,如Customer、Order、Product 等等。而且大致也有点规律:字符串的一般模糊查询,时间日期类的一般按日期查询(忽略时间),其它类型则相等查询。不同 Model 查询的 Action 编码总有八、九分相似,但又不是简单的重复,却又难以重构。
- 需求变动,如增加一个查询条件,修改 View 是必须的,但也要修改 Action,增加一个参数,还要加一行 Where 或 WhereIf。简单变动却多处修改,烦人啊,而且这种需求变动又是比较频繁的,尤其是在项目初期。若能只修改 View 而不修改 Action 就爽了。
- …
思考后,我决定使用 Expression Trees 作为查询 Action的参数来弥补这些不足。
使用 Expression<Func<T, bool>> 作为 Action 的参数
试看如下代码:
1 |
public ActionResult Index3(Expression<Func<Employee, bool>> predicate) {
|
我将 Expression Trees 作为 Action 的唯一的参数(暂不考虑分页、排序等),将所有的查询条件都统一汇集至 predicate 参数。
所有的查询(不管是 Employee 还是 Customer)都使用如上代码。其它实体查询只需修改参数的类型,如 Customer 查询改为 Expression<Func<Customer, bool>> 。
细心品味下,相信你能理解这种做法的精妙之处!
如上修改代码后,直接运行会报错,因为 MVC 中默认的数据绑定器 DefaultModelBinder 不能正确绑定 Expression<Func<T, bool>> 类型的参数。
我们要新创一个新的 ModelBinder。
创建 QueryConditionExpressionModelBinder
我们需要一个新的 ModelBinder 来为 Expression<Func<T, bool>> 类型的参数赋值,且命名为 QueryConditionExpressionModelBinder。
QueryConditionExpressionModelBinder 要根据上下文来自动生成查询的 Expression Trees。主要关注的上下文有两点:首先是当前 Model 的类型,即 typeof(T);其次是 Request 提供的值,可通过 ValueProvider 获取。
下面给出一个粗略实现,仅用来说明这个思路是可行的:
1 |
public class QueryConditionExpressionModelBinder : IModelBinder {
|
了解这段代码,需要 MVC 和 Expression Trees 的一些知识。这段代码还用到了 Expression 扩展方法,参见:《c# 扩展方法奇思妙用基础篇九:Expression 扩展》。
如果不想在 Global.asax 文件中设置 Expression<Func<T, bool>> 的 ModelBinder, 可以借助用下面这个 Attribute 类:
1 |
public class QueryConditionBinderAttribute : CustomModelBinderAttribute {
|
Index3 简单修改如下:
1 |
public ActionResult Index3([QueryConditionBinder]Expression<Func<Employee, bool>> predicate) { //... }
|
下面是一个调试截图,绑定正常。

再次说明:本部分代码仅用来说明思路可行,用了大量的硬编码。
我也正在准备编写一个更加灵活 QueryConditionExpressionModelBinder,来应对复杂的查询(如时间范围、值大于、小于等、以及限制对某些属性的查询),目前也有了一个大体的思路,初步完成后在之后的博文中和大家分享下。如果你有好的思路,不妨写在回复中。
源码下载:MvcQuery.rar (VS2010 MVC3 项目,1758KB)
在线演示:http://asp-net-mvc-expression-trees-as-action-parameter.ldp.me
http://www.cnblogs.com/ldp615/archive/2011/09/16/asp-net-mvc-expression-trees-as-action-parameter.html
ASP.NET MVC:Expression Trees 作为参数简化查询的更多相关文章
- Expression Trees 参数简化查询
ASP.NET MVC 引入了 ModelBinder 技术,让我们可以在 Action 中以强类型参数的形式接收 Request 中的数据,极大的方便了我们的编程,提高了生产力.在查询 Action ...
- 解决Win10系统下 C# DateTime 出现星期几的问题 解决ASP.NET MVC 接受Request Playload参数问题
解决Win10系统下 C# DateTime 出现星期几的问题 昨天晚上写代码的时候偶然发现 DateTime 里出现了星期几,当时一阵凌乱,去网上百度没有详细解决办法,很多人说可以用用 ToStri ...
- ASP.NET MVC 5 学习教程:添加查询
原文 ASP.NET MVC 5 学习教程:添加查询 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 通过控 ...
- 【译】ASP.NET MVC 5 教程 - 8:搜索查询
原文:[译]ASP.NET MVC 5 教程 - 8:搜索查询 添加一个搜索的方法和搜索的视图 在本节中,我们为 Index 方法添加查询功能,使我们能够根据电影的题材或名称进行查找. 修改 Inde ...
- ASP.NET MVC 后台接收集合参数和 jquery ajax 传值
MVC 接收参数数组(集合) 示例样本: public class Person { public string FirstName { get; set; } publi ...
- ASP.NET MVC控制器Controller中参数
前述文章参见:ASP.NET MVC控制器Controller 绪论 之前的控制器返回的均为常量字符串,接下来展示如何获取请求传来的参数,而返回"动态"的字符串. 可以在操作方法B ...
- ASP.NET MVC 给Action的参数赋值的方式
Action指的是Controller类中的方法,如上文中的Index. Action参数的三种常见类型:Model类型.普通参数.FormCollection Model类型 我们可以直接在地址栏后 ...
- ASP.NET MVC利用ActionLink实现动态组合查询
一个文件传输数据表,内有日期.传输协议.传输方向(上传或下载).文件名等信息,完整的表内容显示如下: 现在需要分类查询,即按照协议和传输方向查询.单独的分类查询问题并不大,比如,按协议查询,在View ...
- ASP.NET MVC post请求接收参数的三种方式
1.在控制器中建立一个PostDemo方法,建立视图创建一个表单 <h2>PostDemo</h2> name的值:@ViewBag.name <br /> nam ...
随机推荐
- 娓娓道来c指针 (4)解析c的声明语句
(4)解析c的声明语句 在继续探索c指针之前.有必要来解析下c语言中复杂的声明语法. 仅仅须要记住两则:一个原则,一个规则. 原则:先看标示符. 规则:运算符优先级是规则. 举例说明 1.最简单的 i ...
- 将web容器置于OSGi框架下进行web应用的开发
将web容器置于OSGi框架下,其实就是将web容器做成OSGi支持的Bundle,再安装到OSGi框架中,这里使用的是Jetty容器. 1.创建一个Eclipse插件项目,在此插件下创建一个WebR ...
- RBAC权限设计实例(转)
实现业务系统中的用户权限管理 B/S系统中的权限比C/S中的更显的重要,C/S系统因为具有特殊的客户端,所以访问用户的权限检测可以通过客户端实现或通过客户端+服务器检测实现,而B/S中,浏览器是每一台 ...
- Axure 蚂蚁设计团队组件库 让交互稿美美"搭"
Github资源:https://github.com/ant-design/ant-design-pro English | 简体中文 技术实践篇 https://pro.ant.design/do ...
- Timer Schedule参数说明
Timer是一种定时器工具,用来在一个后台线程计划执行指定任务.它可以计划执行一个任务一次或反复多次. TimerTask一个抽象类,它的子类代表一个可以被Timer计划的任务. schedule的意 ...
- EF性能
批量操作时,加上这句:Context.Configuration.AutoDetectChangesEnabled = false; 主要是取消跟踪,缩短对象添加到上下文的时间(非执行SQL的时间)
- laravel 数据库查询的一些函数意义(未完)
->all() 查询数据库中的全部信息,一对象形式返回 $rows=UserModel::all(); ->get() 查询的执行函数,->toArray()将查询的是数据集转换成数 ...
- (void)0和0的区别及用法
(void)0相当于宏NULL,NULL本身的含义为“空”,在c语言代表“不存在.不确定”的含义. 0不能简单的理解为“没有”的意思,在c语言及二进制中,0和1代表的是“一件事物的正反两个方面“,0是 ...
- Windows API中的坑
本文主页链接:Windows API中的坑 ExpandEnvironmentStrings 风险: 进程会继承其父进程的环境变量.在展开如%APPDATA%等文件夹时,有可能父进程对此环境变量进行过 ...
- CXAnimation类
#include "XAnimation.h" CXAnimation::CXAnimation(void) { m_strName = ""; m_nFram ...