What Is Keyset Pagination?
Keyset Pagination is also known as cursor pagination and is currently one of the fastest pagination techniques in .NET π₯
β Christian Schou
Have you ever tried Keyset Pagination? It's (at the time of writing) one of the fastest pagination techniques when writing .NET code π₯
In this .NET tip I will show you a quick demo on how you can implement keyset pagination and how fast it is compared to offset pagination.
Why Is It Faster? π
When doing traditional pagination we fetch records from the database using an offset and limit for the query.
With Keyset Pagination we are using a sortable property on the query. This could be a sequential ID or date time property, from the previous/latest record in our previous dataset/or what some refer to as page.
By using that technique we can make sure that each page returned from the database, begins exactly where our last page ended.
What Are The Use Cases? π€
By using Keyset Pagination you ensure that your application is able to scale when your datasets do.
It is especially great for making applications with the infinite scroll feature.
If you are making let's say an eCommerce site where people should be able to skip to a specific page using on-site pagination, it would recommend you to stick to the offset pagination logic.
How To Do It π§βπ»
Let's see how to do keyset pagination in .NET.
var books = _dbContext.Books
.Where(b => b.Id > latestId)
.Take(pageSize)
.OrderBy(b => b.Id)
.Select(b => new BookResponseModel(b.Id, b.Name, b.Author))
.ToList();
var booksResponse = new KeysetResponse<List<BookResponseModel>>(
books[^1].Id,
books);
return booksResponse;
That's how simple you can implement and use Keyset Pagination! Looking for an explanation? π€
1. Retrieving Books from the Database
var books = _dbContext.Books
.Where(b => b.Id > latestId)
.Take(pageSize)
.OrderBy(b => b.Id)
.Select(b => new BookResponseModel(b.Id, b.Name, b.Author))
.ToList();
_dbContext.Books
- Accesses theBooks
table from the database context (_dbContext
)..Where(b => b.Id > latestId)
- Now we apply a filter to filters the books to include only those with anId
greater than ourlatestId
. This is a keyset pagination technique, which is efficient for large datasets..Take(pageSize)
- This will limit the number of books retrieved topageSize
..OrderBy(b => b.Id)
- Sorting the books by theirId
in ascending order..Select(b => new BookResponseModel(b.Id, b.Name, b.Author))
- (I personally love this β€οΈ) This will project each book entity into a newBookResponseModel
object containingId
,Name
, andAuthor
properties..ToList()
- Finally converts the result to a list ofBookResponseModel
objects.
2. Creating the Response
var booksResponse = new KeysetResponse<List<BookResponseModel>>(
books[^1].Id,
books);
books[^1].Id
- This will Get theId
of the last book in the retrieved list (books[^1]
uses the C# index from end operator to access the last element, like I explained before).new KeysetResponse<List<BookResponseModel>>(books[^1].Id, books)
- Creates a newKeysetResponse
object with theId
of the last book and the list of books.
Finally we return the response from our query. The BookResponseModel
and KeysetResponse
objects are custom classes I made. This is what it looks like.
public class KeysetResponse<T>
{
public int LastId { get; }
public T Data { get; }
public KeysetResponse(int lastId, T data)
{
LastId = lastId;
Data = data;
}
}
public class BookResponseModel
{
public int Id { get; }
public string Name { get; }
public string Author { get; }
public BookResponseModel(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
}
Conclusion
Keyset pagination is an efficient method for paginating through large datasets, especially when users are expected to navigate sequentially through the data in your application, either forwards or backwards.
This method works by using a reference point, such as the Id
of the last item from the previous page, to fetch the next set of records. Hereβs why it great β¨
- Performance - Keyset pagination is optimized for performance. Since it doesnβt need to scan through skipped rows, it can handle large datasets smoothly without significant slowdowns π
- Consistency - By using a consistent key (like
Id
), it ensures that records are not missed or duplicated as users navigate through the pages π
If you learned something from this .NET Tip, please share it with your friends and developer fellows βοΈ
Resources
My name is Christian. I am a 28-year-old Solution Architect & Software Engineer with a passion for .NET, Cloud, and Containers. I love to share my knowledge and teach other like-minded about tech.