Menu Close

Integrate Paging in ASP.Net Core Web API- Beginner’s Guide

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.

aspnetcore-customer-details-swagger

aspnetcore-customer-api-details

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);
        }
aspnetcore-paging-1
aspnetcore-paging-2

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

SUPPORT ME

Buy Me A Coffee

Leave a Reply

Your email address will not be published. Required fields are marked *