This article will discuss How to integrate Paging in ASP.Net Core Web API. This is the starter guide that helps you understand the concept of paging in ASP.Net Core Web API. Please read my previous article on How to use Policy-based Authorization using JWT in .Net Core 7 and Angular.
Please find the source code in this GitHub Repo.
What is Paging?
Assume you have an API endpoint that might possibly return millions of entries with a single request. Assume there are 100s of users who will exploit this endpoint by requesting all of the data at the same time. This would almost certainly terminate your server and cause a slew of problems, including security.
An ideal API endpoint would allow its users to retrieve only a limited number of records at a time. We are not putting any strain on our Database Server, the CPU on which the API is located, or the network traffic in this manner. This is a critical feature for any API. Especially the open APIs.
Not only would this be an inefficient method of returning results, but it may also have disastrous consequences for the application or the hardware it runs on. Furthermore, each client has limited memory resources and must limit the amount of shown results.
To avoid these repercussions, we need a means to return a fixed number of results to the client.
Creating the ASP.Net Core API application
Here we have created an ASP.Net Core application that fetches the data from the database of customer information.
Create the Customer Model
public class Customer
{
public int CustomerID { get; set; }
public string? Title { get; set; }
public string? FirstName { get; set; }
public string? MiddleName { get; set; }
public string? LastName { get; set; }
public string? CompanyName { get; set; }
public string? EmailAddress { get; set; }
public string? Phone { get; set; }
}
Adding below Packages
Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools
Create the DataContext to implement EF Core
public class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
public DbSet<Customer> customers { get; set; }
}
Adding Connection String
"ConnectionStrings": {
"DefaultConnection": "Server=JAYANTT;Database=JTCommonDB;Trusted_Connection=True;MultipleActiveResultSets=True;Encrypt=False;"
}
Adding the controller that Returns the Customer Info
[Route("api/[controller]")]
[ApiController]
public class CustomersController : ControllerBase
{
private readonly DataContext _context;
public CustomersController(DataContext context)
{
_context = context;
}
// GET: api/Customers
[HttpGet]
public async Task<ActionResult<IEnumerable<Customer>>> Getcustomers()
{
if (_context.customers == null)
{
return NotFound();
}
return await _context.customers.ToListAsync();
}
}
All the changes are done, Run the application and you can see the customer Info in swageer/web browser.
Paging Implementation
We created PagedParameters
a class where we are passing it as an argument to our controller. We are using constant maxPageSize to restrict our API to a maximum of 50 owners. We have two public properties – PageNumber and PageSize. If not set by the caller, PageNumber will be set to 1, and PageSize to 10.
public class PagedParameters
{
const int maxPageSize = 50;
public int PageNumber { get; set; } = 1;
private int _pageSize = 10;
public int PageSize
{
get
{
return _pageSize;
}
set
{
_pageSize = (value > maxPageSize) ? maxPageSize : value;
}
}
}
As you can see, we’ve transferred the skip/take logic to the static method inside of the PagedList class. We’ve added a few more properties, that will come in handy as metadata for our response.
Has previous is true if CurrentPage is larger than 1, and HasNext is calculated if CurrentPage is smaller than the number of total pages. TotalPages is calculated too, by dividing the number of items by the page size and then rounding it to the larger number since a page needs to exist even if there is one item on it.
Now that we’ve cleared that out, let’s change our OwnerRepository and OwnerController accordingly.
public class PagedList<T> : List<T>
{
public int CurrentPage { get; private set; }
public int TotalPages { get; private set; }
public int PageSize { get; private set; }
public int TotalCount { get; private set; }
public bool HasPrevious => CurrentPage > 1;
public bool HasNext => CurrentPage < TotalPages;
public PagedList(List<T> items, int count, int pageNumber, int pageSize)
{
TotalCount = count;
PageSize = pageSize;
CurrentPage = pageNumber;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
AddRange(items);
}
public static PagedList<T> ToPagedList(IQueryable<T> source, int pageNumber, int pageSize)
{
var count = source.Count();
var items = source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList();
return new PagedList<T>(items, count, pageNumber, pageSize);
}
}
Create an Interface
public interface ICustomerRepository : IRepositoryBase<Customer>
{
PagedList<Customer> GetCustomers(PagedParameters custParameters);
Customer GetCustomerById(Guid custId);
}
public interface IRepositoryBase<T>
{
IQueryable<T> FindAll();
IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression);
}
Repository class
The two repository classes that we have created to implement the interface. CustomerRepository returns the data by paging
public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : class
{
protected DataContext RepositoryContext { get; set; }
public RepositoryBase(DataContext repositoryContext)
{
this.RepositoryContext = repositoryContext;
}
public IQueryable<T> FindAll()
{
return this.RepositoryContext.Set<T>()
.AsNoTracking();
}
public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression)
{
return this.RepositoryContext.Set<T>()
.Where(expression)
.AsNoTracking();
}
}
public class CustomerRepository : RepositoryBase<Customer>, ICustomerRepository
{
public CustomerRepository(DataContext repositoryContext)
: base(repositoryContext)
{
}
public PagedList<Customer> GetCustomers(PagedParameters custParameters)
{
return PagedList<Customer>.ToPagedList(FindAll(),
custParameters.PageNumber,
custParameters.PageSize);
}
public Customer GetCustomerById(Guid custId)
{
return FindByCondition(cust => cust.CustomerID.Equals(custId))
.DefaultIfEmpty(new Customer())
.FirstOrDefault();
}
}
Controller action method
Here we have added the Action method that doing paging for us. We need to pass the Page Number and Page size, accordingly it will extract the record.
[HttpGet]
[Route("paging-filter")]
public IActionResult GetCustomerPagingData([FromQuery] PagedParameters custParameters)
{
var customer = _repository.GetCustomers(custParameters);
var metadata = new
{
customer.TotalCount,
customer.PageSize,
customer.CurrentPage,
customer.TotalPages,
customer.HasNext,
customer.HasPrevious
};
Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(metadata));
return Ok(customer);
}
Conclusion
In this article, we discussed How to integrate Paging in ASP.Net Core Web API.
Leave behind your valuable queries and suggestions in the comment section below. Also, if you think this article helps you, do not forget to share this with your developer community. Happy Coding 🙂
Related Articles
- .NET 8 Authentication with Identity in a Web API using Bearer Tokens and Cookies
- How to convert Text To Speech With Azure Cognitive Services using Angular and .Net Core
- CRUD operation using the repository pattern with .Net 8, Ef-Core, and MySQL
- How to use Response Compression in .NET Core
- How to Integrate GraphQL in .Net Core
- Upload Download and Delete files in Azure Blob Storage using ASP.NET Core and Angular
- How to upload files to Azure Blob Storage using Asp.Net Core Web API
- How to store app secrets in ASP.NET Core using Secret Manager
- Logging into Azure App Service with ASP.Net Core
- Integrate Paging in ASP.Net Core Web API- Beginner’s Guide
- Create CRUD Operation using ReactJs and ASP.Net Core API- Starter Template
- How to use Policy-based Authorization using JWT in .Net Core 7 and Angular
- How to use Azure Key Vault in ASP.Net Core
- How to Build ASP.Net Core API using Azure Cosmos DB Local Emulator
- How to Send Emails from ASP.NET Core using Azure
SUPPORT ME