ASP.NET 4過濾數據新控件QueryExtender
在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>
• 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 querywithout filtering
return query;
else
// Return only non-discontinued products
return query.Where(p => p.Discontinued == false);
}
Public Shared Function FilterBasedOnUser(ByVal query As IQueryable(Of Product))
As IQueryable(Of product)
If Roles.IsUserInRole("Administrators") Then
' Return the querywithout filtering
Return query
Else
' Return only non-discontinued products
Return query.Where(Function(p) p.Discontinued = False)
End If
End Function
<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去編寫代碼實現。