找回密码
 FreeOZ用户注册
查看: 3338|回复: 1
打印 上一主题 下一主题

LINQ 之 自定义 排序 分页

[复制链接]
跳转到指定楼层
1#
发表于 13-12-2008 07:29:48 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
提示: 作者被禁止或删除, 无法发言

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?FreeOZ用户注册

x
http://borrell.parivedasolutions.com/2008/01/objectdatasource-linq-paging-sorting.html

一般的分页和排序的例子都是假设你使用了 SqlDataSource 作为数据源,但是在现实世界应用程序中,我们一般采用N层结构,所以你会使用 ObjectDataSource,而不是那个超级傻瓜的 SqlDataSource,显然这个更实用。本例采用Grid显示一个Northwind数据库的一个产品列表(包括 Product Name, Category Name, Supplier Name),还包括基于Category和Supplier的过滤选项。

我们要做的例子是从Northwind数据库中显示一个产品的列表(Product Name, Category Name, Supplier Name)。可以根据Category,Supplier,或者同时使用二者的过滤条件来筛选表格。表格也支持点击任意列排序,同时支持分级排序,就是按着一个级别排序,然后再按着另外一个维度排序。

表格的样子如下图所示:

                               
登录/注册后可看大图


我们使用ObjectDataSource的好处之一是:应用层和数据层分离,你比较容易写测试样例来测试你的代码。本着Test Driven的思想,我们应该先写测试单元,然后再用测试单元测试应用层代码。我们这里使用 NUnit测试框架。

首先在 Pariveda.ObjectDataSource 解决方案里面,建立一个名为 Pariveda.ObjectDataSource.Test的测试解决方案。

                               
登录/注册后可看大图



                               
登录/注册后可看大图


再在 Pariveda.ObjectDataSource 下面增加两个项目 Project:Pariveda.ObjectDataSource (Windows Class Library 类型)和 Pariveda.ObjectDataSource.Web (ASP.NET Web Application类型)

                               
登录/注册后可看大图


下一步是曾加引用 references,Test和Web需要一个指向 Business的引用

                               
登录/注册后可看大图


同时,添加一个到 nunit.framework.dll的引用到 Test项目

首先建立一个 LINQ to SQL Classes 文件到 Business 项目,来读取 Products

                               
登录/注册后可看大图


拖拽表 Category Product Supplier 到新建的 Northwind.dbml中

                               
登录/注册后可看大图


首先,建立一个实体类 entity class 用于存储查询结果到 Business Library中,这个实体类的名称叫 ProductGetListResult 我们仅仅定义了那些我们实际中需要用到的项目 (这一步不一定是LINQ推荐的,其实完全可以使用缺省的 Product 实体类,这样建立CRUD比较容易)
  1. public class ProductGetListResult
  2. {
  3.     public string ProductName { get; set; }
  4.     public string CategoryName { get; set; }
  5.     public string SupplierName { get; set; }
  6. }
复制代码
接下来在Business Library 里建立一个class叫做 ProductController,建立一个main method 叫做 GetList 这个方法包含以下参数:
  • 可选的 categoryId 用于通过 category 进行筛选
  • 可选的 supplierId 用于通过 supplier 进行筛选
  • sortType 用于指出排序关键字
  • startRowIndex 用于指出分页的起始行
  • maximumRows 用于指出调用总行数
  1. public List<ProductGetListResult> GetList(int? categoryId, int? supplierId, string sortType, int startRowIndex, int maximumRows)
  2. {
  3.     using (NorthwindDataContext db = new NorthwindDataContext())
  4.     {
  5.         //First we start with the base query
  6.         var productQuery = from p in db.Products
  7.                            select p;

  8.         //Next we filter if category or supplier has been specified
  9.         productQuery = GetListQuery(productQuery, categoryId, supplierId);

  10.         //Sort the query
  11.         productQuery = GetListSort(productQuery, sortType);

  12.         //Use the Skip and Take methods to limit the results to the page requested
  13.         productQuery = productQuery.Skip(startRowIndex).Take(maximumRows);

  14.         //Project results into a ligher weight class and actually execute the query by
  15.         //calling the ToList method
  16.         return productQuery.Select(p => new ProductGetListResult
  17.                        {
  18.                            ProductName = p.ProductName,
  19.                            SupplierName = p.Supplier.CompanyName,
  20.                            CategoryName = p.Category.CategoryName
  21.                        }).ToList();
  22.     }
  23. }
复制代码
建立一个 DataContext的实例 instance (使用 using(...){...} 语句来确保 data context 和 subsequent 数据连接被关闭),上面的这个方法里,我们一共完成了4件事:

基于跳进进行动态过滤 (categoryId supplierId)
基于sortType 进行动态排序
选取一个起点为 startRowIndex 长度为 maxiumRows 的子集
把选取出来的子集装进一个轻量级的列表 List 叫做 ProductGetListResults

现在我们来看看这个 GetListQuery 方法:
  1. private IQueryable<Product> GetListQuery(IQueryable<Product> productQuery, int? categoryId, int? supplierId)
  2. {
  3.     //Only filter by category id if specified (query not executed here either)
  4.     if (categoryId.HasValue && categoryId.Value > 0)
  5.     {
  6.         productQuery = productQuery.Where(p => p.CategoryID == categoryId.Value);
  7.     }

  8.     //Only filter by supplier id if specified (query not executed here either)
  9.     if (supplierId.HasValue && supplierId.Value > 0)
  10.     {
  11.         productQuery = productQuery.Where(p => p.SupplierID == supplierId.Value);
  12.     }

  13.     //Return the query (Query isn't even executed when we return it)
  14.     return productQuery;
  15. }
复制代码
这个以前使用SQL查询没什么两样,但是使用LINQ,IDE界面支持对强类型的智能感知,而且还避免了SQL注入攻击的危险。下面我们来看看排序方法:
  1. private IQueryable<Product> GetListSort(IQueryable<Product> productQuery, string sortType)
  2. {
  3.     //Determining whether to sort ascending or descending
  4.     //(GridView appends DESC if the column is clicked on twice to indicate a descending sort)
  5.     bool sortDescending = false;
  6.     if (!String.IsNullOrEmpty(sortType))
  7.     {
  8.         string[] values = sortType.Split(' ');
  9.         sortType = values[0];
  10.         if (values.Length > 1)
  11.         {
  12.             sortDescending = values[1] == "DESC";
  13.         }
  14.     }

  15.     switch (sortType)
  16.     {
  17.         case "CategoryName":
  18.             if (sortDescending)
  19.             {
  20.                 productQuery = productQuery.OrderByDescending(p => p.Category.CategoryName)
  21.                     .ThenBy(p => p.ProductName); ;
  22.             }
  23.             else
  24.             {
  25.                 productQuery = productQuery.OrderBy(p => p.Category.CategoryName)
  26.                     .ThenBy(p => p.ProductName); ;
  27.             }
  28.             break;
  29.         case "SupplierName":
  30.             if (sortDescending)
  31.             {
  32.                 productQuery = productQuery.OrderByDescending(p => p.Supplier.CompanyName)
  33.                     .ThenBy(p => p.ProductName);
  34.             }
  35.             else
  36.             {
  37.                 productQuery = productQuery.OrderBy(p => p.Supplier.CompanyName)
  38.                     .ThenBy(p => p.ProductName);
  39.             }
  40.             break;
  41.         default:
  42.             if (sortDescending)
  43.             {
  44.                 productQuery = productQuery.OrderByDescending(p => p.ProductName);
  45.             }
  46.             else
  47.             {
  48.                 productQuery = productQuery.OrderBy(p => p.ProductName);
  49.             }
  50.             break;
  51.     }

  52.     //The query has not executed during this method, it is only setting up the query for execution
  53.     return productQuery;
  54. }
复制代码
我实际上在这里同时完成了两件事,首先看看是否有一个倒序标记DESC包含在sortType参数里,GridView自动添加一个 DESC到 sortType参数。用户连续两次点击标题栏,第一次是正序,第二次是倒序。注意,我们这里实际上匹配了二级的排序,CategoryName ProductName, SupplierName ProductName

这里使用LINQ最大的一个好处是自始至终这段代码还没有被执行:延迟执行(直到使用ToList方法)。

为了使 ObjectDataSource 支持分页,我们还需要一个方法 GetListCount 我们只是返回了一个自己给 ObjectDataSource GridView需要知道一共有多少行,这样才能够正确显示页数。
  1. public int GetListCount(int? categoryId, int? supplierId)
  2. {
  3.     using (NorthwindDataContext db = new NorthwindDataContext())
  4.     {
  5.         var productQuery = from p in db.Products
  6.                            select p;
  7.         productQuery = GetListQuery(productQuery, categoryId, supplierId);
  8.         return productQuery.Count();
  9.     }
  10. }
复制代码
这里好像我们实际上进行了两次类似的查询,一次是为了返回数据子集,另一次是为了返回数据自己的记录数。
这里我们还建立了另外两个 controller 类到 ProductController:CategoryController, SupplierController
一切准备就绪,现在开始调用这个Business层了。
我们实际上不需要编写任何后台代码就可以调用 ObjectDataSource 来建立网页,我们这里使用了 MasterPage 和 AJAX 我们还是用了 App_Themes 来定义GridView的外观
首先,添加两个下拉菜单: CategoryDropDownList SupplierDropDownList

                               
登录/注册后可看大图


                               
登录/注册后可看大图


                               
登录/注册后可看大图


                               
登录/注册后可看大图


                               
登录/注册后可看大图


注意我们这里选择了Value和Key都来自于 business controller类。

                               
登录/注册后可看大图


向页面添加一个GridView,设置新数据源

                               
登录/注册后可看大图


选择Object作为数据源,命名为 ProductObjectDataSource

                               
登录/注册后可看大图



                               
登录/注册后可看大图


选取 GetList方法

                               
登录/注册后可看大图



                               
登录/注册后可看大图


修改DropDownList,添加一个 All 选项到UI层界面中。
  1. <asp:GridView ID="GridView1" runat="server" AllowPaging="True" EnableSortingAndPagingCallbacks="True"

  2.     AllowSorting="true" Width="600px" AutoGenerateColumns="False" DataSourceID="ProductObjectSource">

  3.     <Columns>

  4.         <asp:BoundField DataField="ProductName" HeaderText="<%$ Resources:ObjectDataSource, ProductLabel %>" SortExpression="ProductName"

  5.             ItemStyle-Width="250px" HeaderStyle-HorizontalAlign="Left" />

  6.         <asp:BoundField DataField="CategoryName" HeaderText="<%$ Resources:ObjectDataSource, CategoryLabel %>" SortExpression="CategoryName"

  7.             ItemStyle-Width="100px" HeaderStyle-HorizontalAlign="Left" />

  8.         <asp:BoundField DataField="SupplierName" HeaderText="<%$ Resources:ObjectDataSource, SupplierLabel %>" SortExpression="SupplierName"

  9.             ItemStyle-Width="250px" HeaderStyle-HorizontalAlign="Left" />

  10.     </< span>Columns>

  11.     <EmptyDataTemplate>

  12.         <asp:Localize runat="server" meta:resourcekey="EmptyData" />

  13.     </< span>EmptyDataTemplate>

  14. </< span>asp:GridView>
复制代码

                               
登录/注册后可看大图
  1. <asp:ObjectDataSource ID="ProductObjectSource" runat="server" EnablePaging="True"
  2.     OldValuesParameterFormatString="original_{0}" [color=Red]SelectCountMethod="GetListCount"[/color]
  3.     [color=Blue]SelectMethod="GetList"[/color] [color=Red]SortParameterName="sortType"[/color] [color=Red]TypeName="Pariveda.ObjectDataSource.Business.ProductController"[/color]>
  4.     [color=Red]<SelectParameters>[/color]
  5.         <asp:ControlParameter ControlID="CategoryDropDownList" DefaultValue="null" Name="categoryId"
  6.             PropertyName="SelectedValue" Type="Int32" />
  7.         <asp:ControlParameter ControlID="SupplierDropDownList" DefaultValue="null" Name="supplierId"
  8.             PropertyName="SelectedValue" Type="Int32" />
  9.     </< span>SelectParameters>
  10. </< span>asp:ObjectDataSource>
复制代码
To add to the look and feel, use ASP.NET theming to set the styles of both the GridView and the general layout of the page.
Next we can easily add AJAX functionality so we don’t get those pesky postbacks.  First we can use the built-in GridView functionality to switch pages and sorts without postbacks.  Then we put the GridView inside and AJAX UpdatePanel and set the triggers to be based on the DropDownLists.
  1. <asp:ObjectDataSource ID="ProductObjectSource" runat="server" EnablePaging="True"

  2.     OldValuesParameterFormatString="original_{0}" SelectCountMethod="GetListCount"

  3.     SelectMethod="GetList" SortParameterName="sortType" TypeName="Pariveda.ObjectDataSource.Business.ProductController">

  4.     <SelectParameters>

  5.         <asp:ControlParameter ControlID="CategoryDropDownList" DefaultValue="null" Name="categoryId"

  6.             PropertyName="SelectedValue" Type="Int32" />

  7.         <asp:ControlParameter ControlID="SupplierDropDownList" DefaultValue="null" Name="supplierId"

  8.             PropertyName="SelectedValue" Type="Int32" />

  9.     </< span>SelectParameters>

  10. </< span>asp:ObjectDataSource>
复制代码
Hopefully, this was easy enough to follow.  Once you get the hang of it and get over some of the initial hurdles, it is easy to maintain and apply to different applications.

Click here to download the source for these examples.
回复  

使用道具 举报

2#
 楼主| 发表于 15-12-2008 02:11:56 | 只看该作者
提示: 作者被禁止或删除, 无法发言
这是一个非常好的教程,再结合CRUD的例子,甚至把GridView换成ListView,就强大而入时啦。
回复  

使用道具 举报

您需要登录后才可以回帖 登录 | FreeOZ用户注册

本版积分规则

小黑屋|手机版|Archiver|FreeOZ论坛

GMT+10, 4-9-2026 15:09 , Processed in 0.019325 second(s), 18 queries , Gzip On, Redis On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表