Pagination in .NET Core / 5 / 6 MVC with Entity Framework Core
10/20/2021
Author
Gal RatnerPagination is useful with large results sets. There are plenty of solutions out there to create next and previous buttons, but I couldn’t find anything that creates a nice Bootstrap paginator so I just wrote my own:
To use it first add a The PaginatedList class to your code. This is a Modified version of the one found on MSDN.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; namespace Messages.Utilities { public class PaginatedList<T> : List<T> { public int PageIndex { get; private set; } public int TotalPages { get; private set; } public PaginatedList(List<T> items, int count, int pageIndex, int pageSize) { PageIndex = pageIndex; TotalPages = (int)Math.Ceiling(count / (double)pageSize); this.AddRange(items); } public bool HasPreviousPage { get { return (PageIndex > 1); } } public bool HasNextPage { get { return (PageIndex < TotalPages); } } public static async Task<PaginatedList<T>> CreateAsync( IQueryable<T> source, int pageIndex, int pageSize) { var count = await source.CountAsync(); var items = await source.Skip( (pageIndex - 1) * pageSize) .Take(pageSize).ToListAsync(); return new PaginatedList<T>(items, count, pageIndex, pageSize); } public static PaginatedList<T> Create( List<T> source, int pageIndex, int pageSize) { var count = source.Count(); var items = source.Skip( (pageIndex - 1) * pageSize) .Take(pageSize).ToList(); return new PaginatedList<T>(items, count, pageIndex, pageSize); } } }
Next add the _Pagination partial view to your shared views folder
<nav aria-label="Blog Pages"> <ul class="pagination justify-content-center"> <li class="page-item @(Model.PageIndex <= 1 ? "disabled" : string.Empty)"><a class="page-link" asp-route-pageNumber="1">First</a></li> <li class="page-item @(!Model.HasPreviousPage ? "disabled" : string.Empty)"><a class="page-link" asp-route-pageNumber="@(Model.PageIndex - 1)">Previous</a></li> @for (int i = Model.PageIndex - 3; i <= Model.PageIndex - 1; i++) { @if (i < 1) continue; <li class="page-item"><a class="page-link" asp-route-pageNumber="@i">@i</a></li> } @for (int i = Model.PageIndex; i <= Model.PageIndex + 3; i++) { @if (i > Model.TotalPages) break; <li class="page-item @(Model.PageIndex == i ? "active" : string.Empty)"><a class="page-link" asp-route-pageNumber="@i">@i</a></li> } <li class="page-item @(!Model.HasNextPage ? "disabled" : string.Empty)"><a class="page-link" asp-route-pageNumber="@(Model.PageIndex + 1)">Next</a></li> <li class="page-item @(Model.PageIndex >= Model.TotalPages ? "disabled" : string.Empty)"><a class="page-link" asp-route-pageNumber="@(Model.TotalPages)">Last</a></li> </ul> </nav>
Now you are ready to use the PaginatedList from your controller
[Route("Blog/")] public async Task<IActionResult> Index(int? pageNumber) { int pageSize = 20; return View(PaginatedList<BlogPost>.Create(await _cache.GetBlogPostsAync(), pageNumber ?? 1, pageSize)); }
And finally add the paginator to your view
<partial name="~/Views/Shared/_Paginator.cshtml" />
The result should be a nice Bootstrap paginatior with page numbers, next, previous, first and last buttons.
Did you find this post useful? Visit my software consulting firm Inverted Software