在 asp.net mvc中的简单分页算法 ,续

在上个月发表的 http://www.cnblogs.com/bwangel/p/mvcpager.html 中,讨论了一下asp.net mvc中结合Entity framework框架进行的分页,包括服务端实现和客户端的分部视图的实现。在一个月过后,发现服务器端的代码每次都写一堆,非常冗长,基于职业习惯,想着把服务器端的分页代码也能封装一下,这是以前的代码:

        const int PAGE_SZ = 10;
        [Route("{id:int}/{page:int=1}")]
        public ActionResult Index(int? id, int? page)
        {
              var articles = (id == null || id == 0) ? _db.Articles : _db.Articles.Where    (art => art.CatalogId == id);
          var recordCount = articles.Count();

               articles = articles.OrderByDescending(art => art.EditTime)
              . Skip((page.Value - 1) * PAGE_SZ)
             .Take(PAGE_SZ);

              ViewBag.Pager = new Pager()
              {
                   PageIndex = page.Value,
                PageSize = PAGE_SZ,
                  RecordCount = recordCount,

};

  return View(articles);
}        

经过一翻折腾,在原有的Pager类中增加一个static方法:

        /// <summary>
        /// 根据指定的集合表达式和排序字段得出一个分页对象和分页后的数据集
        /// </summary>
        /// <typeparam name="T">实体的类型</typeparam>
        /// <typeparam name="TSort">用于排序的属性类型</typeparam>
        /// <param name="allList">初始的集合</param>
        /// <param name="orderExpress">排序的表达式</param>
        /// <param name="page">页号</param>
        /// <param name="pageSize">页数</param>
        /// <returns></returns>
        public static Pager GetPagedList<T, TSort>(ref IQueryable<T> allList,
            Expression<Func<T, TSort>> orderExpress, int? page, int pageSize = 20)
        {
            int p = Math.Max(1, page == null ? 1 : page.Value);
            var recordCount = allList.Count();
            allList = allList.OrderByDescending(orderExpress)
                .Skip((p - 1) * pageSize)
                .Take(pageSize);

            return new Pager()
            {
                PageIndex = p,
                PageSize = pageSize,
                RecordCount = recordCount,
            };
        }

这样,上述应用层的调用就简化为:

        public ActionResult Index(int? id, int? page)
        {
            var articles = (id == null || id == 0) ? _db.Articles : _db.Articles.Where(art => art.CatalogId == id);
//此方法要返回两个值,一个是分页对象,一个是分页后的集合。所以后者用ref传地址。 art=>art.Id是表示根据Id排序.
ViewBag.Pager = Pager.GetPagedList(ref articles, art => art.Id, page); return View(articles); }

虽然GetPagerList方法有两个泛型类型参数,但根据编译器的智能化处理,我们在调用时可以省略显示声明T和TSort,让编译器自动判断。

这样调用显得非常简洁。

当然此方法还可以做进一步封装,把条件查询表达式也一起封进去,但那样方法的定义会显得非常复杂和冗长,以现有的应用规模不划算,暂时没这必要。

经过了如此多的封装,Entity Framework是否能足够聪明,能高效智能地查询出我们想要的结果呢?我不太放心,特地跟踪了一下,发现

它和我们预期的表现完全一致,就两条语句,一条是整体计数,一条是具体的分页:

SELECT 
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[CT_Article] AS [Extent1]
    )  AS [GroupBy1]

SELECT TOP (20) 
    [Extent1].[Id] AS [Id], 
    [Extent1].[ParentId] AS [ParentId], 
    [Extent1].[Title] AS [Title], 
    [Extent1].[Text] AS [Text], 
    [Extent1].[Intro] AS [Intro], 
    [Extent1].[CatalogId] AS [CatalogId], 
    [Extent1].[AuthorId] AS [AuthorId], 
    [Extent1].[ReplyCount] AS [ReplyCount], 
    [Extent1].[HitCount] AS [HitCount], 
    [Extent1].[PostTime] AS [PostTime], 
    [Extent1].[LastReplyTime] AS [LastReplyTime], 
    [Extent1].[Status] AS [Status]
    FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[ParentId] AS [ParentId], [Extent1].[Title] AS [Title], [Extent1].[Text] AS [Text], [Extent1].[Intro] AS [Intro], [Extent1].[CatalogId] AS [CatalogId], [Extent1].[AuthorId] AS [AuthorId], [Extent1].[ReplyCount] AS [ReplyCount], [Extent1].[HitCount] AS [HitCount], [Extent1].[PostTime] AS [PostTime], [Extent1].[LastReplyTime] AS [LastReplyTime], [Extent1].[Status] AS [Status], row_number() OVER (ORDER BY [Extent1].[Id] DESC) AS [row_number]
        FROM [dbo].[CT_Article] AS [Extent1]
    )  AS [Extent1]
    WHERE [Extent1].[row_number] > 0
    ORDER BY [Extent1].[Id] DESC