Aniket.EntityFrameworkCore.Pagination
1.0.0
dotnet add package Aniket.EntityFrameworkCore.Pagination --version 1.0.0
NuGet\Install-Package Aniket.EntityFrameworkCore.Pagination -Version 1.0.0
<PackageReference Include="Aniket.EntityFrameworkCore.Pagination" Version="1.0.0" />
<PackageVersion Include="Aniket.EntityFrameworkCore.Pagination" Version="1.0.0" />
<PackageReference Include="Aniket.EntityFrameworkCore.Pagination" />
paket add Aniket.EntityFrameworkCore.Pagination --version 1.0.0
#r "nuget: Aniket.EntityFrameworkCore.Pagination, 1.0.0"
#:package Aniket.EntityFrameworkCore.Pagination@1.0.0
#addin nuget:?package=Aniket.EntityFrameworkCore.Pagination&version=1.0.0
#tool nuget:?package=Aniket.EntityFrameworkCore.Pagination&version=1.0.0
Aniket.EntityFrameworkCore.Pagination
A lightweight and reusable pagination library for ASP.NET Core applications using Entity Framework Core.
The library provides a simple ToPagedListAsync() extension method for IQueryable<T> that handles pagination, total record calculation, total page calculation, and pagination parameter validation.
Features
- Generic pagination using
IQueryable<T> - Works with Entity Framework Core
- Asynchronous database operations
- Calculates total number of records
- Calculates total number of pages
- Returns current page and page size
- Validates pagination parameters
- Configurable through simple extension methods
- Works with any entity type
- Keeps pagination logic out of controllers and services
Requirements
- .NET 10
- Entity Framework Core 10
Installation
Install the package using the .NET CLI:
dotnet add package Aniket.EntityFrameworkCore.Pagination
Or add the package reference manually to your .csproj:
<PackageReference Include="Aniket.EntityFrameworkCore.Pagination" Version="1.0.0" />
Basic Usage
Assume you have an Entity Framework Core DbContext:
public class AppDbContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
public AppDbContext(
DbContextOptions<AppDbContext> options)
: base(options)
{
}
}
Import the pagination extension:
using Aniket.EntityFrameworkCore.Pagination.Extensions;
You can then paginate an Entity Framework Core query:
var result = await _context.Employees
.ToPagedListAsync(page, pageSize);
For example:
var result = await _context.Employees
.ToPagedListAsync(2, 10);
This requests:
- Page 2
- 10 records per page
Filtering Before Pagination
Because the extension method works with IQueryable<T>, you can compose your query before applying pagination.
var result = await _context.Employees
.Where(e => e.Salary > 50000)
.ToPagedListAsync(2, 10);
The filtering is applied before pagination.
You can also use ordering:
var result = await _context.Employees
.Where(e => e.Salary > 50000)
.OrderBy(e => e.Name)
.ToPagedListAsync(2, 10);
How It Works
The pagination flow is:
IQueryable<T>
|
| CountAsync()
↓
Total Items
|
| Skip()
| Take()
↓
Current Page Items
|
| ToListAsync()
↓
PagedResult<T>
For example:
var result = await _context.Employees
.ToPagedListAsync(2, 10);
Internally, the library performs the equivalent of:
var totalItems = await query.CountAsync();
var items = await query
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
It then calculates the total number of pages and returns a PagedResult<T>.
Response Structure
The method returns:
PagedResult<T>
The result contains:
| Property | Description |
|---|---|
Items |
Records belonging to the requested page |
Page |
Current page number |
PageSize |
Maximum number of records requested per page |
TotalItems |
Total number of matching records |
TotalPages |
Total number of available pages |
Example:
{
"items": [
{
"id": 11,
"name": "Employee 11"
},
{
"id": 12,
"name": "Employee 12"
}
],
"page": 2,
"pageSize": 10,
"totalItems": 125,
"totalPages": 13
}
The example above shows only two items for brevity. A page with pageSize = 10 can contain up to 10 items.
Generic Support
PagedResult<T> is generic, so the library can be used with any entity type.
For employees:
PagedResult<Employee>
For products:
PagedResult<Product>
For orders:
PagedResult<Order>
For example:
var products = await _context.Products
.ToPagedListAsync(1, 20);
And:
var orders = await _context.Orders
.ToPagedListAsync(3, 25);
Pagination Rules
The library validates the supplied pagination parameters.
Page
The page number must be greater than or equal to 1.
Valid:
page = 1
page = 2
page = 10
Invalid:
page = 0
page = -1
An invalid page number throws:
ArgumentOutOfRangeException
Page Size
The page size must be greater than or equal to 1.
Valid:
pageSize = 1
pageSize = 10
pageSize = 100
Invalid:
pageSize = 0
pageSize = -1
An invalid page size throws:
ArgumentOutOfRangeException
Maximum Page Size
The current default maximum page size is:
100
Therefore:
pageSize = 100
is valid, while:
pageSize = 101
throws:
ArgumentOutOfRangeException
This prevents clients from accidentally requesting extremely large result sets.
Empty Results
If the query doesn't contain any matching records, the library returns an empty Items collection.
For example:
{
"items": [],
"page": 1,
"pageSize": 10,
"totalItems": 0,
"totalPages": 0
}
Last Page
The last page may contain fewer records than the requested page size.
For example, if there are:
125 total records
pageSize = 10
there are:
13 total pages
The pages contain:
Page 1 → 1–10
Page 2 → 11–20
...
Page 12 → 111–120
Page 13 → 121–125
Therefore, pageSize represents the maximum number of records per page, not a guarantee that every page contains exactly that number.
Why IQueryable<T>?
The extension method accepts:
IQueryable<T>
rather than:
List<T>
This allows Entity Framework Core to compose the query and execute pagination at the database level.
For example:
var result = await _context.Employees
.Where(e => e.Salary > 50000)
.OrderBy(e => e.Name)
.ToPagedListAsync(2, 10);
The filtering, ordering, skipping, and taking can be translated by Entity Framework Core into a database query.
This avoids loading the entire table into application memory before applying pagination.
Complete Controller Example
using Aniket.EntityFrameworkCore.Pagination.Extensions;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class EmployeesController : ControllerBase
{
private readonly AppDbContext _context;
public EmployeesController(AppDbContext context)
{
_context = context;
}
[HttpGet]
public async Task<IActionResult> GetEmployees(
int page = 1,
int pageSize = 10)
{
var result = await _context.Employees
.OrderBy(e => e.Id)
.ToPagedListAsync(page, pageSize);
return Ok(result);
}
}
A request such as:
GET /api/employees?page=2&pageSize=10
returns the second page of employees.
Error Handling
The library validates pagination arguments and throws standard .NET exceptions when invalid values are supplied.
Example:
await _context.Employees
.ToPagedListAsync(0, 10);
throws:
ArgumentOutOfRangeException
Applications can handle these exceptions using their existing ASP.NET Core exception-handling or validation middleware.
Testing
The library is tested using xUnit and Entity Framework Core with SQLite.
The test suite covers:
- Successful pagination
- Correct page number
- Correct page size
- Correct number of returned items
- Correct total item count
- Correct total page count
- Invalid page numbers
- Invalid page sizes
- Maximum page size validation
Run the tests with:
dotnet test
Building the Package
To build the project:
dotnet build
To create the NuGet package:
dotnet pack
The generated package will be placed under:
bin/Release/
Example Pagination
Given 125 employees:
Page size = 10
The result is:
Page 1 → Employees 1–10
Page 2 → Employees 11–20
Page 3 → Employees 21–30
...
Page 12 → Employees 111–120
Page 13 → Employees 121–125
The metadata for page 2 is:
{
"page": 2,
"pageSize": 10,
"totalItems": 125,
"totalPages": 13
}
Package Structure
Aniket.EntityFrameworkCore.Pagination
│
├── Extensions
│ └── QueryableExtensions.cs
│
├── Models
│ └── PagedResult.cs
│
├── README.md
└── Aniket.EntityFrameworkCore.Pagination.csproj
Design Goals
The library is designed to:
- Keep pagination logic reusable
- Reduce repeated
Skip()andTake()code - Keep pagination logic out of controllers
- Work with generic entity types
- Execute pagination at the database level
- Provide consistent pagination metadata
- Keep the API simple
License
This project is licensed under the MIT License.
See the LICENSE file for details.
Author
Aniket Madaan
Version
Current version:
1.0.0
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.EntityFrameworkCore (>= 10.0.11)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 110 | 8/25/2026 |