可以创建可重复使用的分页。我们需要的是:
- 部分视图用于存储页面数量和指向它们的链接
- 包含
IQueryable 的分页逻辑的可重用方法
The complete example with source code can be seen here.
所以我们的代码是这样的:
人员表。我用过 SQL Server:
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
我使用了 DatabaseFirst 方法。这是 Entity Framework 生成的类。
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<System.DateTime> ModifyDate { get; set; }
public string Modifier { get; set; }
}
这是一个包含数据和分页信息的类:
public class DataResultViewModel<T>
{
/// <summary>
/// Data items
/// </summary>
public IEnumerable<T> Items { get; set; }
/// <summary>
/// Pagination
/// </summary>
public Pagination Pagination { get; set; }
}
这是包含有关人员信息的类。
public class PersonViewModel
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
/// <summary>
/// What route should be requested while paging
/// </summary>
public class RouteInfo
{
/// <summary>
/// Name of controller
/// </summary>
public string ControllerName { get; set; }
/// <summary>
/// Name of action
/// </summary>
public string ActionName { get; set; }
}
和分页类:
public class Pagination
{
/// <summary>
/// Total count of items
/// </summary>
public int TotalItems { get; set; }
/// <summary>
/// Count of items at the page
/// </summary>
public int PageSize { get; set; } = 5;
/// <summary>
/// Current page
/// </summary>
public int Page { get; set; }
/// <summary>
/// Sorted by field name
/// </summary>
public string SortBy { get; set; }
/// <summary>
/// Is this an ascending sort?
/// </summary>
public bool IsSortAscending { get; set; }
/// <summary>
/// Information about what page should be requested
/// </summary>
public RouteInfo RouteInfo { get; set; }
}
以及进行分页的类。基本上,Skip() 和 Take() 是进行分页的方法,所以我们需要对所有IQueryable 使用这些方法。 C# 有一个非常简洁的特性,叫做扩展方法。扩展方法允许重用代码:
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();
}
// GET: Person
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>
就是这样!