---
title: "Easy Pagination for Entity Framework in 3 steps"
description: "Pagination is the process of dividing a set into discrete pages. In the context of Entity Framework, that means we are only getting a certain amount of entries from the database.\n\nAnd we will implement a very easy solution to make that happen in 3 steps. The result will look like this:\n```csharp\nvar pagedList = DbContext.BlogPosts.ToPagedList(page: 1, pageSize: 5);\n```"
updated: 2023-01-11
tags: [".NET", "Entity Framework", "RavenDB", "C#", "EF"]
canonical: "https://steven-giesel.com/blogPost/09285b33-79e6-4879-95e0-35aeae5fbcc6/easy-pagination-for-entity-framework-in-3-steps"
---

# Easy Pagination for Entity Framework in 3 steps

Pagination is the process of dividing a set into discrete pages. In the context of Entity Framework, that means we are only getting a certain amount of entries from the database.

And we will implement a very easy solution to make that happen in 3 steps.

Not only that, pagination should also allow us to get the possible amount of entries in the database so that we can indicate to the user: "Hey, there is a next page!".

## 1. Create the object
First we need some kind of object that a) holds all of our objects and b) holds the meta information:

```csharp
public class PagedList<T> : IReadOnlyList<T>
{
    private readonly IList<T> subset;
    public PagedList(IEnumerable<T> items, int count, int pageNumber, int pageSize)
    {
        PageNumber = pageNumber;
        TotalPages = (int)Math.Ceiling(count / (double)pageSize);
        subset = items as IList<T> ?? new List<T>(items);
    }

    public int PageNumber { get; }

    public int TotalPages { get; }

    public bool IsFirstPage => PageNumber == 1;

    public bool IsLastPage => PageNumber == TotalPages;

    public int Count => subset.Count;

    public T this[int index] => subset[index];

    public IEnumerator<T> GetEnumerator() => subset.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => subset.GetEnumerator();
}
```

A few notes here:
 * We implement `IReadOnlyList` so the consumer has no way of tempering with the data itself
 * It also gives us the possibility to expose an indexer `myList[2]`
 * The more information you need, the more you can expose here. I just started with some properties like `IsFirstPage` and  `PageNumber`. You could also expose the total number of pages if you wish
 * The underlying data structure is an internal list, that holds all of our information
 * A small remark to the constructor: `items as IList<T>` is quite unsafe if used in the wrong context. If someone from the outside would add objects to `items` our internal list would see those changes as well. So make sure that this constructor is only used by the upcoming method I'll show you. An easy way of ensuring that is to make the constructor internal and place it next to the extension method (I'll show in a few seconds) in a separate assembly.

## 2. Extension method
To make the integration as easy as possible we will use an extension method:
```csharp
public static class PagedListQueryableExtensions
{
    public static async Task<PagedList<T>> ToPagedListAsync<T>(
    this IQueryable<T> source,
    int page,
    int pageSize,
    CancellationToken token = default)
    {
        var count = await source.CountAsync(token);
        if (count > 0)
        {
            var items = await source
                .Skip((page - 1) * pageSize)
                .Take(pageSize)
                .ToListAsync(token);
            return new PagedList<T>(items, count, page, pageSize);
        }

        return new(Enumerable.Empty<T>(), 0, 0, 0);
    }
}
```

Remarks here:
 * Super easy we sit on top of `IQueryable` so the usage will be extremely simple
 * As said earlier, we retrieve the count from the database to determine if we have other pages
 * You might want to change `ToListAsync` to `ToList` if you have a `NVARCHAR(MAX)` or binary column in your table. There is a open bug about that: https://stackoverflow.com/questions/28543293/entity-framework-async-operation-takes-ten-times-as-long-to-complete/28619983

## 3. Usage
Now the easy part - how to use that. As we used an extension method, we can just use `ToPagedListAsync` as last call instead of `ToListAsync` in our db context.

```csharp
var pagedList = await db.BlogPosts.ToPagedListAsync(1, 15);
```

You can for sure also use it in combination with all the other LINQ methods:
```csharp
await db.BlogPosts
 .Where(bp => bp.IsPublished)
 .Where(bp => bp.Title.Contains(searchTerm))
 .OrderBy(bp => bp.PublishDate)
 .ToPagedListAsync(1, 15);
```

Of course, I only loaded here the first page. If you have a website and the user wants to have the next page, you can load this via `ToPagedListAsync(2, 15);` and so on. You can also disable or enable buttons based on whether or not there is a previous or next page.

## Extra: `IEnumerable`
The shown code works with Entity Framework but one could make it easily possible to work with types like `IEnumerable<T>`. You only have to adopt the extension method, or add a new one:
```csharp
public static PagedList<T> ToPagedList<T>(
    this IEnumerable<T> source,
    int page,
    int pageSize)
    {
        var count = await source.Count(token);
        if (count > 0)
        {
            var items = source
                .Skip((page - 1) * pageSize)
                .Take(pageSize)
                .ToList();
            return new PagedList<T>(items, count, page, pageSize);
        }

        return new(Enumerable.Empty<T>(), 0, 0, 0);
}
```

This implementation uses your goto stuff from the `System.Linq` namespace.

## Extra: RavenDB
You can do the same with **RavenDB** as well. Here instead of `IQueryable<T>` we are using `IRavenQueryable<T>`:
```csharp
public static async Task<IPagedList<T>> ToPagedListAsync<T>(
        this IRavenQueryable<T> source,
        int pageIndex, int pageSize,
        CancellationToken token = default)
{
    var count = await source.CountAsync(token);
    if (count > 0)
    {
        var items = await source
            .Skip((pageIndex - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync(token);
        return new PagedList<T>(items, count, pageIndex, pageSize);
    }

    return PagedList<T>.Empty;
}
```

Keep in mind that you have to have an asynchronous document session: `using var session = 
 documentStore.OpenAsyncSession();`.

## Conclusion
Pagination doesn't have to be difficult. Here I showed an easy way of implementing the backend logic for pagination. This blog post basically uses a similar implementation to what I showed you. As I have multiple storage providers available like (`RavenDb`, `SqlServer`), there are also multiple extensions for the respective implementation.

## Resources
* Source code to this blog post: [here](https://github.com/linkdotnet/BlogExamples/tree/main/PaginationEF)
* All my sample code is hosted in this repository: [here](https://github.com/linkdotnet/BlogExamples)