【IT168 专稿】在ASP.NET 4中的一个新的控件是QueryExtender。QueryExtender控件是为了简化LinqDatasource或EntityDataSource控件返回的数据过滤而设计的,它主要是将过滤数据的逻辑从数据控件中分离出来。使用QueryExtender是十分容易的事,只需要简单往页面上增加一个QueryExtender控件,指定其数据源是哪个控件并设置过滤条件就可以了。比如,当在页面中显示产品的信息时,你可以使用该控件去显示那些在某个价格范围的产品,也可以搜索用户指定名称的产品。
当然,不使用QueryExtender控件的话,LinqDataSource和EntityDataSource也是可以过滤数据的。这两个控件都有一个where的属性,可以指定过滤数据的条件。QueryExtender提供的是一种更简单的方式去过滤数据。本文包含了一系列的例子(可以,在本文最后下载),展示了QueryExtender的强大威力。
首先要注意的是,QueryExtender控件只能配合LinqDataSource和EntityDatasource使用。它不能去过滤SqlDataSource控件的数据集。当然,使用该控件的话,你必须使用LINQ或者ADO.NET Entity框架。对这两者的讨论已经超出本文讨论的范围。简而言之,LINQ和Entity Framework是微软的对象关系映射工具,使用的是LinqDataSource和EntityDataSource两个控件,它们允许开发通过ORM的解决方案去访问数据库。
本文附录的代码下载中,提供的例子使用的是ADO.NET Entity框架,并对Northwind数据库中的Products,Categories和Suppliers三张表进行了实体建模。其中数据库文件Northwind.mdf位于网站的App_Data目录下,App_Code目录下则有ADO.NET Entity框架的建模文件Northwind.edmx,它定义了对象实体和数据库表之间的关系。
正如在本文介绍部分所说的,QueryExtender控件是配合LinqDatasource 和EntityDataSource 控件使用的。考察一个页面,允许用户输入一个最小的价格 ,之后就能显示小于或等于这个价格的所有产品。这个很容易用如下代码实现:
$<asp:TextBox ID="txtMaxPrice" runat="server"></asp:TextBox>
<asp:Button ID="btnDisplayProducts" runat="server" Text="Show Products" />
<asp:GridView ID="gvProducts" runat="server" DataSourceID="dsProducts" ...>
...
</asp:GridView>
<asp:EntityDataSource ID="dsProducts" runat="server"
ConnectionString="name=NorthwindEntities"
DefaultContainerName="NorthwindEntities" EnableFlattening="False"
EntitySetName="Products" Where="it.UnitPrice <= @UnitPrice">
<WhereParameters>
<asp:ControlParameter ControlID="txtMaxPrice" Name="UnitPrice"
PropertyName="Text" Type="Decimal" />
</WhereParameters>
</asp:EntityDataSource>
在上面的例子中,当用户在txtMaxPrice文本框中输入价格并点“Show Products”按钮后,会产生一个postback。在postback事件中,gridview与dsProducts这个数据源控件绑定,并查询Products表的内容,应用where条件去筛选出那些价格低于或等于在txtMaxPrice文本框输入的价格的产品。这个筛选可以象上文那样设置,也可以在程序中设置,只需要修改EntityDataSource 控件的selecting事件即可。
使用EntityDataSource或者LinqDataSource两个控件的筛选逻辑去完成筛选有两个缺点。首先,它们都仅提供了有限的筛选功能,当然,有时对单一条件的筛选是可以应付的,但如果面对的是多条件筛选,比如要显示价格低于某个数值并且产品名,供应商或者目录包含某个指定的字符串,象这样的复杂条件的话是难实现的。另外一个缺点是过滤的逻辑与数据获取的逻辑功能都混杂在同一个控件中,维护其来比较麻烦。
ASP.NET 4中的新控件QueryExtender,能让我们可以设置更多更复杂的筛选条件。更为重要的是,使用该控件能将数据的获取和数据的筛选两部分逻辑清晰的分离―使用EntityDataSource或者LinqDataSource去获得数据,QueryExtender则专门负责过滤数据,甚至可以设置如何去对结果集进行排序。
使用QueryExtender 控件
我们接下来看下该控件是如何做的,首先我们建立一个页面是列出所有的产品, 之后我们使用QueryExtender去列出产品中的一个部分。我们建立一个简单的页面,象刚才一样,将gridview绑定到EntityDataSource控件中去,如下代码所示:
...
</asp:GridView>
<asp:EntityDataSource ID="dsProducts" runat="server"
ConnectionString="name=NorthwindEntities" EntitySetName="Products"
DefaultContainerName="NorthwindEntities" EnableFlattening="False">
</asp:EntityDataSource>
接着,往页面中增加一个QueryExtender控件。QueryExtender控件在工具面板的Data面板中,与gridview,EntityDataSource控件都是在一起的。接着,指定QueryExtender的数据源ID属性,表明要筛选的是哪个数据源控件的数据,这通过设置
QueryExtender的TargetControlID去设置,我们这里设置的是dsProducts,如下:
</asp:QueryExtender>
我们现在可以开始设置QueryExtender的筛选逻辑了。QueryExtender的筛选逻辑使用的是一个或多个表达式。ASP.NET提供了如下表达式,包括:
• RangeExpression – 获得小于,等于或者大于指定值的数据集合
• SearchExpression –根据给出的字符串去进行搜索
• ControlFilterExpression – 根据指定的控件的值去搜索筛选,比如根据TextBox或者DropDownList
• CustomExpression 和 MethodExpression - 这两个表达式基本基本是一样的,它允许开发者去编写自己的筛选逻辑。
• OrderByExpression – 根据开发者的指定,去排序列和根据指定的方向去排序
比如,去显示单价小于10美元的产品,我们可以增加一个RangeExpression,如下:
<asp:RangeExpression DataField="UnitPrice" MinType="Inclusive" MaxType="Exclusive">
<asp:Parameter DefaultValue="0" />
<asp:Parameter DefaultValue="10" />
</asp:RangeExpression>
</asp:QueryExtender>
在上面的例子中,请注意其中的inclusive和exclusive属性。因为我们想看到的是大于或者等于0美元,但小于10美元的,所以我们对最小值设置的是inclusive包含的属性,对最大值设置的是exclusive不包含的属性。
注意,RangeExpression能够包含所有能在SqlDataSource, LinqDataSource和EntityDataSource控件中使用的参数控件,包括:
• Parameter 控件,用来筛选硬编码的值
• SessionParameter 控件,根据session的值去筛选
• ControlParameter 控件,根据指定控件的值去筛选
例如,要根据用户在文本框输入的值去筛选,我们只需要替换RangeExpression中的第2个参数为ControlParameter,即可:
<asp:RangeExpression DataField="UnitPrice" MinType="Inclusive" MaxType="Exclusive">
<asp:Parameter DefaultValue="0" />
<asp:ControlParameter ControlID="TextBoxID" DefaultValue="0" />
</asp:RangeExpression>
</asp:QueryExtender>
更详细的可以参考本文附录代码中的FilterDemo.aspx文件。
实际上,如果你懂得SQL,就应该知道,QueryExtender控件实际上是在后端为你生成了象如下这样的代码,以上文讲到的例子为例:
FROM Products
WHERE UnitPrice >= 0 AND UnitPrice < 10
使用SearchExpression根据字符串去筛选
QueryExtender控件和SearchExpression使到很容易数据库中根据字符串去筛选数据。SearchExpression允许你指定在一列或多列中去筛选符合指定字符串的数据,可以指定筛选出以某个字符串开头,结尾或者包含的数据集合。下图显示了用户可以在搜索框中输入字符串,页面会显示出产品名称,目录或供应商的名称中以该字符串开头的所有数据。比如在搜索框中输入字符a,则返回的结果中,象Acme Tea,Acme Water,Alice Mutton和Aniseed Syrup都显示出来,而Charteuse verte也显示出来,因为它的供应商Aux joyeux ecclésiastiques,也是以a开头的。
这个例子也是很简单,除了使用gridview和EntityDataSource控件外,指定了SearchExpression去获得产品名称,目录名和供应商名,指定了搜索模式是StartsWith,即以字母开头的方式,并指定了根据文本框输入的内容去筛选。代码如下:
<asp:SearchExpression DataFields="ProductName, Category.CategoryName, Supplier.CompanyName" SearchType="StartsWith">
<asp:ControlParameter ControlID="txtFilterText" />
</asp:SearchExpression>
</asp:QueryExtender>
具体的可以参考本文附送代码中的Search.aspx文件
如果你想筛选出符合者三个条件的所有记录,则可以指定三个SearchExpression即可如下所示:
<asp:SearchExpression DataFields="ProductName" SearchType="StartsWith">
<asp:ControlParameter ControlID="txtFilterText" />
</asp:SearchExpression>
<asp:SearchExpression DataFields="Category.CategoryName" SearchType="StartsWith">
<asp:ControlParameter ControlID="txtFilterText" />
</asp:SearchExpression>
<asp:SearchExpression DataFields="Supplier.CompanyName" SearchType="StartsWith">
<asp:ControlParameter ControlID="txtFilterText" />
</asp:SearchExpression>
</asp:QueryExtender>
这样,指定了三个SearchExpression表达式,它们的逻辑关系是AND。
用代码实现筛选
QueryExtender控件通过使用RangeExpression和SearchExpression使得定义筛选逻辑变得很容易。同样,可以使用CustomExpression或者MethodExpression控件去通过代码的方式去实现筛选逻辑。实际上,当需要应用筛选逻辑时,CustomExpression会触发一个事件,你可以在这个事件里编写相关的处理代码。而MethodExpression则有点不同,你要编写一个静态的方法,并传入一个类型为IQueryable的对象并且返回的对象也是要相同的类型。传入该方法的对象实际上是在筛选逻辑前的一个查询实例,可以在其中编写你需要实现的查询条件,最后要返回这个查询实例。
下面我们更详细去了解下MethodExpression。假设你要根据登陆了的用户的身份去筛选数据,比如管理员能看到所有产品信息但非管理员只能看到没打折的产品。则我们如上所述在静态方法中,传入IQueryable实例作为参数,返回的也对象类型也必须是IQueryable类型的,如下:
public static IQueryable FilterBasedOnUser(IQueryable query)
{
if (Roles.IsUserInRole("Administrators"))
// Return the query without filtering
return query;
else
// Return only non-discontinued products
return query.Where(p => p.Discontinued == false);
}
' VB
Public Shared Function FilterBasedOnUser(ByVal query As IQueryable(Of Product)) As IQueryable(Of product)
If Roles.IsUserInRole("Administrators") Then
' Return the query without filtering
Return query
Else
' Return only non-discontinued products
Return query.Where(Function(p) p.Discontinued = False)
End If
End Function
而在前端的代码中,则只需要使用MethodExpression指出引用的方法即可,如下所示:
<asp:QueryExtender ID="qeByMethod" runat="server" TargetControlID="dsProducts">
<asp:MethodExpression MethodName="FilterBasedOnUser" />
</asp:QueryExtender>
对返回的结果进行排序
除了筛选数据外,QueryExtender控件还可以对筛选出来的数据集指定排列的顺序。这可以通过OrderByExpression去实现,只需要设定哪些字段是需要排序的并指出其排序的方向即可。比如要对ProductName进行升序排序的话,只需要如下实现即可:
<asp:MethodExpression MethodName="FilterBasedOnUser" />
<asp:OrderByExpression DataField="ProductName" Direction="Ascending" />
</asp:QueryExtender>
你也可以设置零个或多个ThenBy的标签,以说明对列排序时的次序,比如下面的例子说明了在产品名称相同的情况下,将根据单价的升序去排列。
<asp:MethodExpression MethodName="FilterBasedOnUser" />
<asp:OrderByExpression DataField="ProductName" Direction="Ascending">
<asp:ThenBy DataField="UnitPrice" Direction="Descending" />
</asp:OrderByExpression>
</asp:QueryExtender>
总结
ASP.NET 4中新提供的控件QueryExtender,简化了对数据的筛选,甚至还可以指定筛选出来的数据的排序。其中RangeExpression允许开发者指定筛选数据的方式,而筛选的逻辑可以通过使用CustomExpression或MethodExpression去编写代码实现。