Paging in ASP.NET Core
Paging in ASP.NET Core is the process of dividing large set of data into manageable "pages" to improve performance, reduce server load, and enhance user experience.
Implementation Strategies
Offset-Based Pagination
The most common approach, using Page Number, Page Size and Total Row Count.
It uses the LINQ Skip and Take operators, which translate to SQL OFFSET and FETCH/LIMIT.
Keyset (Seek-Based) Pagination
Uses a unique key i.e. ID from the last record of the current page to fetch the next set.
It uses a Where clause (e.g., Where(p => p.CustomerId > lastId)) instead of skipping rows.
Here I will be making use of Offset-Based Pagination approach.
Software Information
Database
Here I am making use of Microsoft’s Northwind Database. You can download it from here.
Model
The Model class consists of following properties.
public class Customer
{
public string CustomerID { get;set; }
public string ContactName { get;set; }
public string City { get;set; }
public string Country { get;set; }
}
Database Context
Once the
Entity Framework is configured and connected to the database table, the Database Context will look as shown below.
using Microsoft.EntityFrameworkCore;
namespace Paging_EF_Core_MVC
{
public class DBCtx : DbContext
{
public DBCtx(DbContextOptions<DBCtx> options) : base(options)
{
}
public DbSet<Customer> Customers { get;set; }
}
}
Model for fetching Customer records and implementing Paging
The following Model class named CustomerModel consists of three properties.
1. Customers – List collection of the Customer Model which will hold the records of the Customers.
2. CurrentPageIndex – Holds the value of the index of the Current Page.
3.
PageCount – This value is calculated using the
Maximum Rows to be displayed and the Total Records present in the
Table.
namespace Paging_EF_Core_MVC.Models
{
public class CustomerModel
{
/// <summary>
/// Gets or sets Customers.
/// </summary>
public List<Customer> Customers { get;set; }
/// <summary>
/// Gets or sets CurrentPageIndex.
/// </summary>
public int CurrentPageIndex { get;set; }
/// <summary>
/// Gets or sets PageCount.
/// </summary>
public int PageCount { get;set; }
}
}
Controller
The Controller consists of following Action methods.
Action Method for handling GET operation
Inside this Action method, the GetCustomers method is called with the currentPage parameter value passed as 1, as when the View is accessed for the first time the records of the first page will be displayed and the View is returned.
GetCustomers method
The GetCustomers method accepts currentPage parameter. It has a fixed variable named maxRows which determines the maximum records to be displayed per page.
Inside the
GetCustomers method, first an object of the
CustomerModel class is created and then the records are fetched from the
Customers table using
Entity Framework and are set to the
Customers property of the
CustomerModel object.
The PageCount value is calculated by dividing the count of the records by the maximum rows to be displayed.
The currentPage parameter value is assigned to the CurrentPageIndex property.
The Paging is performed on the records using the Skip and Take functions.
Skip function
The Skip function accepts the Start Index from the set of records to fetched i.e. if Page Index is 1 then the Start Index will be ( 1 - 1) * 10 = 0.
Example: If Current Page Index is 1 and the Maximum Rows is 10, then the Start Index will be (1 - 1) * 10 = 0
Take function
The Take function will fetch the rows based on the value of the maxRows variable.
Action Method for handling POST operation
Inside this Action method, the value of the CurrentPageIndex is fetched from the Request.Form collection and is passed to the GetCustomers method and the View is returned.
public class HomeController : Controller
{
private DBCtx Context { get; }
public HomeController(DBCtx _context)
{
this.Context = _context;
}
public IActionResult Index()
{
return View(this.GetCustomers(1));
}
[HttpPost]
public IActionResult Index(int currentPageIndex)
{
return View(this.GetCustomers(currentPageIndex));
}
private CustomerModel GetCustomers(int currentPage)
{
int maxRows = 10;
CustomerModel customerModel = new CustomerModel();
customerModel.Customers = (from customer in this.Context.Customers
select customer)
.OrderBy(customer => customer.CustomerID)
.Skip((currentPage - 1) * maxRows)
.Take(maxRows).ToList();
double pageCount = (double)((decimal)this.Context.Customers.Count() /Convert.ToDecimal(maxRows));
customerModel.PageCount = (int)Math.Ceiling(pageCount);
customerModel.CurrentPageIndex = currentPage;
return customerModel;
}
}
View
HTML Markup
Inside the View, in the very first line the CustomerModel class is declared as Model for the View.
The
ASP.Net TagHelpers is inherited inside the View.
The View consists of an
HTML Form which has been created using following
ASP.Net Tag Helpers attributes.
asp-action – Name of the Action. In this case the name is Export.
asp-controller – Name of the Controller. In this case the name is Home.
method – It specifies the Form Method i.e. GET or POST. In this case it will be set to POST.
Displaying the Records
For displaying the records, an
HTML Table is used. A loop will be executed over the
Customers property of the
CustomerModel class which will generate the
HTML Table rows with the Customer records.
Implementing Pager
For building the Pager, a FOR loop is executed over the
PageCount property and an
HTML Table is generated with
HTML Anchor.
The
HTML Anchor is assigned a
JavaScript function named
PagerClick in the
HREF attribute. When the
HTML Anchor is clicked
PagerClick JavaScript function is executed.
Also, there is an
HTML HiddenField element inside the form.
Inside the
PagerClick JavaScript function, the Index of the clicked
Pager button is set into the
HiddenField and then submits the form.
@using Paging_EF_Core_MVC.Models
@model CustomerModel
@addTagHelper*, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<form asp-action="Index" asp-controller="Home" method="post">
<h4>Customers</h4>
<hr />
<table cellpadding="0" cellspacing="0">
<tr>
<th>CustomerID</th>
<th>ContactName</th>
<th>City</th>
<th>Country</th>
</tr>
@foreach (Customer customer in Model.Customers)
{
<tr>
<td>@customer.CustomerID</td>
<td>@customer.ContactName</td>
<td>@customer.City</td>
<td>@customer.Country</td>
</tr>
}
</table>
<br />
<table cellpadding="0" cellspacing="0">
<tr>
@for (int i = 1; i <= Model.PageCount; i++)
{
<td>
@if (i != Model.CurrentPageIndex)
{
<a href="javascript:PagerClick(@i);">@i</a>
}
else
{
<span>@i</span>
}
</td>
}
</tr>
</table>
<input type="hidden" id="hfCurrentPageIndex" name="currentPageIndex" />
</form>
<script type="text/javascript">
function PagerClick(index) {
document.getElementById("hfCurrentPageIndex").value = index;
document.forms[0].submit();
}
</script>
</body>
</html>
Screenshot
Testing Log
Compiled in: Visual Studio 2026
Framework: .Net Core 10.
Result: 100% Success
Downloads