动态Entity Framework查询:Dynamic Query 介绍
动态Entity Framework查询:Dynamic Query 介绍
Dynamic Query是一个支持动态Entity Framework查询的库。它的设计初衷是为了减少在管理系统中大量出现的对一个数据集进行查找、排序、分页的这类场景的开发工作量,其设计思想是”markup is code ”。通过在View上编写具有语义信息的标签,来实现这类常见的功能需求,而不再需要额外的代码。它不是一个完整的ORM,是基于Entity Framework的,因此开发者仍然可以利用Entity Framework提供的大量特性,和现有的EF项目保持兼容。
Dynamic Query分为两个部分,其中之一是一个查询接口,这是一个IQueryable<T>的扩展方法:
public static IQueryable<T> Query<T>(this IQueryable<T> data, QueryDescriptor descriptor)
其中QueryDescriptor是一个简单的类,包含了一个查询的必要信息,例如,筛选条件,排序信息,分页信息等。
例如:
QueryDescriptor descriptor = new QueryDescriptor { OrderBy = new OrderByClause { Key = "Price", Order = OrderSequence.ASC }, PageSize = 3, PageIndex = 1, Conditions=new QueryCondition[] { new QueryCondition { Key = "Name",Value = "Rice", Operator = QueryOperator.CONTAINS } } }; int pageCount; var res=ctx.Products.Query(descriptor, out pageCount);
这相当于执行了查询:
select * from Product where [Name] like N'%Rice%' order by Price asc
并且对结果进行分页,每页3条数据,返回第一页。注意,这里返回的结果是IQueryable<T>,这实际上是一个Entity Framework的查询,在没有序列化之前,并没有对数据库进行操作,分页也是发生在服务器端的,这对于大数据来说能够极大的减少网络传输和内存使用量。 当然,手动构造这样一个QueryDescriptor也是一件非常无趣的事情,为此Dynamic Query还为asp.net MVC 实现了一系列的helper方法和一个model binder,来自动生成QueryDescriptor。我们最终的目标是获得页面提交的数据,自动生成QueryDescriptor对象。为此,需要注册一下一个自定义的binder,只需要在Global.asax的Application_Start中添加一行代码:
ModelBinders.Binders.Add(typeof(QueryDescriptor), new QueryDescriptorBinder());
假如我们有如下的EF模型:
我们来实现一个列表,对Product的Name进行筛选。这时候,可以使用QueryTextbox扩展方法来生成查询字段,View的代码如下:
<div class="container"> <form class=".form-search"> @Html.QueryTextbox("Name", "Product Name", QueryOperator.CONTAINS) <input type="submit" value="Search" class="btn"/> </form> </div> <div class="row"> <div class="span12 offset2 "> <table class="table table-striped"> <thead> <tr> <td>ID</td> <td>Category</td> <td>Name</td> <td>Price</td> <td>Description</td> </tr> </thead> <tbody> @foreach (var p in @Model) { <tr> <td>@p.Id</td> <td>@p.Category.Name</td> <td>@p.Name</td> <td>@p.Price</td> <td>@p.Description</td> </tr> } </tbody> </table> </div> </div>
上半部分是一个form,里面有一个QueryTextBox,下半部分就是一个列表,非常简单。看对应的Action:
public ActionResult Index(QueryDescriptor descriptor) { ShopContainer ctx = new ShopContainer(); var result = ctx.Products.Query(descriptor); return View("Product",result); }
由于Model Binder的存在,Action会从页面获得QueryDescriptor的信息。这样一个筛选页面就做好了。如果客户说,我还要增加对种类名称和价格范围的筛选,那需要改什么地方?只需要在View的form中添加几个QueryTextbox就可以了。
<form class=".form-search"> @Html.QueryTextbox("Name", "Product Name", QueryOperator.CONTAINS) @Html.QueryTextbox("Category.Name", "Product Category", QueryOperator.CONTAINS) @Html.QueryTextbox("Price.1", "Price Between", QueryOperator.GREATEROREQUAL,"decimal") @Html.QueryTextbox("Price.2", "", QueryOperator.LESSOREQUAL,"decimal") <input type="submit" value="Search" class="btn"/> </form>
注意,Price出现了两次,需要加上数字后缀区分一下就可以,如果不是string类型,加上类型的说明,这样就OK。Action方法是不需要有任何改动的。
如果客户说这个要分页怎么办? 分页需要稍微多些两行代码,但是也只需要2分钟就足够,先看Action方法:
public ActionResult Product(QueryDescriptor descriptor) { descriptor.PageSize = 5; descriptor.OrderBy = new OrderByClause { Key = "Id", Order = OrderSequence.ASC }; ShopContainer ctx = new ShopContainer(); int pageCount; var result = ctx.Products.Query(descriptor, out pageCount); Pager pager = new Pager(pageCount, descriptor); ViewBag.Pager = pager; return View("Index",result); }
首先指定一页显示的数量,因为要分页,必须要有排序信息,这里是根据Id,升序排列。接下来还是调用Query方法获得数据,注意,这里是Query的一个重载的方法,能够返回总共有多少页,这一般是分页控件需要的信息。接下来实例化一个Pager对象,这个Pager是一个分页器,包含在Dynamic Query中,如果你想用其他第三方的分页器,也是可以的。Pager需要的额外信息就是总页数,把这个Pager放到ViewBag上面,然后Action的工作就完成了。View上面也不想要任何改变,如果你想加上分页链接的话,只需要一行代码:
@Html.QueryPager((Pager)ViewBag.Pager);
看看效果:
项目主页和源代码在:http://dynamicquery.codeplex.com/ 上面有一个样例程序,更多文档在完善中。
这个项目才开始没多久,还有很多细节需要完善,主要是样式、支持更多的控件,比如checkbox,dropdownlist等。以后慢慢补充。