适用于ASP.NET Core MVC的分页列表类

前言

  不知道大家在写MVC网页程序的时候有没有遇到这样的困扰:我想用一个分页列表来查询一页的数据并显示,这种东西第一个想法肯定是去用别人封装好的第三方组件啦,但是看了使用文档后觉得“我不过是用个分页列表,有必要这么麻烦么?”,于是打算自己封装,又发现设计实现上困难重重,于是我写下这篇文章,与大家分享我这个还是非常稚嫩的分页列表类,从参考来源到设计实现再到如何使用。这个分页列表类不需要在容器里注册,这样有个好处是你要用这个就直接建个类文件copy我的代码然后在你的项目中使用就行了。

参考来源

  参考来源源自我之前在写asp.net mvc(.net framework)的时候借鉴的分页列表实现,具体可以看这篇桂大佬写的文章
  这位大佬写的分页列表类有好也有坏。先说好的,在后端action里返回这个封装好的NewList给页面,页面接收这个NewList,NewList里带了很多有用的信息,这样的好处是在前端传递请求的页索引参数(即第几页)时就可以利用模板引擎引用NewList里的信息,如果是前后端分离的项目,这样的操作肯定是不允许的,但这是MVC啊,通常都是一两个人开发,那既然能用一套语言就搞定前后端,why not?
  再说不好的地方,首先是NewList的构造函数,竟然直接传整一个表的集合进来,用户每次点击一下“上一页”或者“下一页”,都要传进来这个表所有的数据,注意是所有!这在我看来是无法接受的,这也是我要优化这个分页列表的主要原因。还有一个原因是,这个NewList没有实现先根据某个字段排序再分页获取数据的功能,而这应该是我们非常非常常用的功能了。
  因此,本人基于“去其糟泊,取其精华”的思想理念,在前人的基础上对其作品加以优化改进。

设计与实现

  首先,我们要保证前端请求的页索引必须在页范围内,比如这个表的数据总共100条,每页10条,那么就有10页,那么在前端能请求的页索引必须在1~10内,低于1的请求1,高于10的请求10。这样做的好处是,后端不需要做这个索引是否超出界限的判定,只需要返回对应该页索引的数据即可(单一职责)。我们可以先定义一个用于传递必要数据的ViewModel,利用这个model里的信息做判定,那么这个model里必须包括哪些信息呢?

  1. 要显示这一页的数据,必须有IEnumerable
  2. 考虑“首页”的实现,请求页索引的参数为1(假设页索引从1开始)
  3. 考虑“上一页”的实现,若请求的页索引的参数小于1,则设置该参数为1
  4. 考虑“下一页”的实现,若请求的页索引参数大于总页数,则设置该参数为总页数。所以需要“总页数”信息
  5. 考虑“尾页”的实现,请求“总页数”

ViewModel

    public class PageListViewModel
    {
       //这里的Student替换为你的context里的某个领域类
        public IEnumerable<Student> Students { get; set; }
        public int PageIndex { get; set; }
        public int TotalPage { get; set; }
    }

前端页面

@model MvcPagingListDesign.Models.PageListViewModel

<table>
    @foreach (var stu in Model.Students)
    {
        <tr>
            <td>@stu.Number</td>
            <td>@stu.Name</td>
        </tr>
    }
</table>

<a asp-controller="Home" asp-action="TurnToPage" asp-route-pageIndex="1">首页</a>
<a asp-controller="Home" asp-action="TurnToPage" asp-route-pageIndex="@(Model.PageIndex-1 < 1 ? 1 : Model.PageIndex-1)">上一页</a>
<a asp-controller="Home" asp-action="TurnToPage" asp-route-pageIndex="@(Model.PageIndex+1 > Model.TotalPage ? Model.PageIndex : Model.PageIndex+1)">下一页</a> <!--  三目运算符真香~ -->
<a asp-controller="Home" asp-action="TurnToPage" asp-route-pageIndex="@(Model.TotalPage)">尾页</a>

  接下来就是我们的重头戏:pagelist的实现。为了能让不同的dbcontext和不同的领域类使用,所以必须采用泛型实现。然后排序的实现是很简单的就是对应的方法里传入一个expression参数,使用者调用方法时只需像使用Linq里的方法语法一样,传入一个lambda表达式即可。下面我们看具体实现。

PageList类

public class MvcPagingList<TContext,TClass> where TContext : DbContext where TClass : class
    {
        private TContext _context;
        //public int PageIndex { get;private set; }
        public int PageSize { get;private set; }
        public int TotalCount { get;private set; }
        public int TotalPage { get; private set; }
        public MvcPagingList(TContext context,int pageSize)
        {
            _context = context;
            TotalCount = _context.Set<TClass>().Count();
            PageSize = pageSize;
            TotalPage = (int)Math.Ceiling(TotalCount / (double)PageSize);
        }
        /// <summary>
        /// 不排序获取对应页索引的分页数据
        /// </summary>
        /// <param name="pageIndex"></param>
        /// <returns></returns>
        public IEnumerable<TClass> GetPageTable(int pageIndex)
        {
            return  _context.Set<TClass>().Skip((pageIndex - 1) * PageSize)
                .Take(PageSize).AsEnumerable();
        }
        /// <summary>
        /// 根据TKey字段进行升序,再获取对应页索引的分页数据
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="pageIndex"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        public IEnumerable<TClass> GetPageTableByAsc<TKey>
            (int pageIndex, Expression<Func<TClass, TKey>> expression)
        {
            return _context.Set<TClass>().OrderBy<TClass, TKey>(expression)
                .Skip((pageIndex - 1) * PageSize).Take(PageSize).AsEnumerable();
        }
        /// <summary>
        /// 根据TKey字段进行降序,再获取对应页索引的分页数据
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="pageIndex"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        public IEnumerable<TClass> GetPageTableByDesc<TKey>
            (int pageIndex, Expression<Func<TClass, TKey>> expression)
        {
            return _context.Set<TClass>().OrderByDescending<TClass, TKey>(expression)
                .Skip((pageIndex - 1) * PageSize).Take(PageSize).AsEnumerable();
        }
    }

  如果上面的代码你看不太懂,可以去补下C#的语法知识包括linq,lambda还有泛型。(我相信你可以看懂的,只是抽象封装而已)

如何使用

  也许这才是你们最关心的问题,所以我专门搞个标题出来(嘿嘿嘿)。前端页面代码不做改变,我们只看后端代码,具体怎么使用我直接写在注释里送给你。

        public IActionResult TurnToPage(int pageIndex)
     {
         //初始化,由于是泛型类,所以传入你需要的领域类以及该领域类所在的context
         var pagelist = new MvcPagingList<StuContext, Student>(_context, 4); //参数一是你注入的context对象,参数二表示每页多少个
         //调用分页方法,参数一表示页索引即第几页,参数2表示你要根据领域类的哪个字段进行升序排序
         var stus = pagelist.GetPageTableByAsc(pageIndex, stu => stu.Number);
         //封装成ViewModel
         var model = new PageListViewModel
         {
             Students = stus,
             PageIndex = pageIndex,
             TotalPage = pagelist.TotalPage
         };
         //将model返回给页面
         return View(model);
     }

结语

  至此,相信你们应该都明白怎么使用了(如果还是不太明白的话clone我的例子来运行下,可以的话顺手给个star哦~),也非常欢迎大家来给我的demo提提意见,不过我也是个.NET新手,太高端的技术我也不会哈哈!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值