分页功能实现指南

在开发Web应用程序时,分页是一个常见且重要的需求。本文将介绍如何在不使用第三方库的情况下,实现一个可复用、可定制的分页功能。

在探索了众多教程和库之后,发现了一个功能齐全的库,但它在查询时会锁定数据库,这并不适合。因此,对分页功能的要求如下:

  • 代码可复用
  • 不依赖第三方库
  • 可以放入部分视图中,并可定制
  • 代码专注于分页,不创建Repository层(将在另一篇文章中介绍)

使用代码

假设正在开发一个人员信息的Web应用程序。使用SQL Server创建了一个名为“Person”的表:

IF NOT EXISTS ( SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Person' AND TABLE_SCHEMA = 'dbo' ) BEGIN CREATE TABLE dbo.Person ( ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY, CreateDate DATETIME NOT NULL DEFAULT GETDATE(), Creator VARCHAR(100) NOT NULL, ModifyDate DATETIME NULL, Modifier VARCHAR(20) NULL, FirstName VARCHAR(150) NOT NULL, LastName VARCHAR(1000) NOT NULL ) ON [PRIMARY] END GO

使用了Entity Framework的DatabaseFirst方法,生成了以下类:

public partial class Person { public int ID { get; set; } public System.DateTime CreateDate { get; set; } public string Creator { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Nullable ModifyDate { get; set; } public string Modifier { get; set; } }

接下来,定义视图模型类,包含数据和分页信息:

public class DataResultViewModel<T> { public IEnumerable<T> Items { get; set; } public Pagination Pagination { get; set; } } public class PersonViewModel { public int ID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } public class RouteInfo { public string ControllerName { get; set; } public string ActionName { get; set; } } public class Pagination { public int TotalItems { get; set; } public int PageSize { get; set; } = 5; public int Page { get; set; } public string SortBy { get; set; } public bool IsSortAscending { get; set; } public RouteInfo RouteInfo { get; set; } }

然后,定义一个扩展方法类,用于实现分页:

public static class IQueryableExtension { public static IQueryable<T> UseOrdering<T, TResultSelector>(this IQueryable<T> query, Pagination pagination, Expression<Func<T, TResultSelector>> field) { if (string.IsNullOrWhiteSpace(pagination.SortBy) || string.IsNullOrEmpty(pagination.SortBy)) return query; return pagination.IsSortAscending ? query.OrderBy(field) : query.OrderByDescending(field); } public static IQueryable<T> UsePagination<T>(this IQueryable<T> query, Pagination pagination) { if (pagination.Page <= 0) pagination.Page = 1; if (pagination.PageSize <= 0) pagination.PageSize = 10; return query.Skip((pagination.Page - 1) * pagination.PageSize).Take(pagination.PageSize); } }

接下来是服务层的实现:

public class PersonService { public DataResultViewModel<PersonViewModel> GetWithPagination(Pagination pagination, Expression<Func<Person, DateTime>> fieldName) { var result = new DataResultViewModel<PersonViewModel>(); using (var db = new MiscellaneousEntities()) { var persons = db.Person.AsQueryable(); result.Pagination = pagination; result.Pagination.TotalItems = persons.Count(); result.Pagination.RouteInfo = new RouteInfo() { ActionName = "Index", ControllerName = "Person" }; if (pagination.SortBy == null) pagination.SortBy = "CreateDate"; persons = persons.UseOrdering(pagination, fieldName); persons = persons.UsePagination(pagination); result.Items = persons.Select(s => new PersonViewModel { ID = s.ID, FirstName = s.FirstName, LastName = s.LastName }).ToList(); return result; } } }

最后是控制器的实现:

public class PersonController : Controller { PersonService _personService; public PersonController() { _personService = new PersonService(); } public ActionResult Index(int? page, int? pageSize, string sortBy, bool? isSortAscending) { return View(_personService.GetWithPagination(new Pagination { Page = page ?? 1, PageSize = pageSize ?? 3, SortBy = sortBy, IsSortAscending = isSortAscending ?? false }, v => v.CreateDate)); } }

接下来,需要创建视图。这是一个需要分页的人员视图:

@model OnlyPagination.ViewModel.DataResultViewModel<OnlyPagination.ViewModel.PersonViewModel> <div class="d-flex justify-content-center"> <div> @foreach (var item in Model.Items) { <div> <p>Id is @item.ID</p> <p>FirstName is @item.FirstName</p> <p>LastName is @item.LastName</p> </div> <hr /> } </div> </div> <div class="d-flex justify-content-center"> @{ @Html.Partial("Pagination", Model.Pagination) } </div>

这是一个可复用的分页部分视图:

@model OnlyPagination.Extensions.Query.Model.Pagination @{ var pagesCount = Math.Ceiling((decimal)Model.TotalItems / (decimal)Model.PageSize); var pages = new List<int>(); for (var i = 1; i <= pagesCount; i++) { pages.Add(i); } } <div> <p class="d-flex justify-content-center"> Page @Model.Page of @pagesCount </p> <ul class="pagination"> <li class="page-item @(Model.Page == 1 ? "disabled" : "")"> <a aria-label="Previous" class="page-link" href="@Url.Action(Model.RouteInfo.ActionName, Model.RouteInfo.ControllerName, new { page = Model.Page - 1, pageSize = Model.PageSize })"> <span aria-hidden="true">«</span> </a> </li> @for (int pageNumber = 1; pageNumber <= pages.Count; pageNumber++) { <li class="page-item @(Model.Page == pageNumber ? "active" : "")"> <a class="page-link" href="@Url.Action(Model.RouteInfo.ActionName, Model.RouteInfo.ControllerName, new { page = pageNumber, pageSize = Model.PageSize })"> @pageNumber </a> </li> } <li class="page-item @(Model.Page == pages.Count ? "disabled" : "")"> <a aria-label="Next" class="page-link" href="@Url.Action(Model.RouteInfo.ActionName, Model.RouteInfo.ControllerName, new { page = Model.Page + 1, pageSize = Model.PageSize })"> <span aria-hidden="true">»</span> </a> </li> </ul> </div>
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485