Pagination in .NET Core / 5 / 6 MVC with Entity Framework Core
Posted: 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