Pagination in C#: Complete Guide with Easy Code Examples 📚

Juan España
ByteHide
Published in
6 min readMay 24, 2024

Today, we’re diving into pagination in C#. Imagine showing a library with thousands of books — you wouldn’t want your users to scroll endlessly, right? This is where pagination saves the day! We’ll explore what pagination is, why it’s important, and common scenarios where you’ll need it.

Introduction to Pagination in C#

What is Pagination?

Pagination is a technique to split large data sets into smaller, manageable chunks or pages. Think of it like reading a book — instead of viewing the entire book at once, you read one page at a time.

Why Use Pagination in Web Applications?

Pagination helps improve user experience by loading only a subset of data at a time. It reduces load times, decreases server stress, and makes navigating data way easier for users.

Common Scenarios for Pagination

  • List of Products in an E-commerce Store
  • Search Results from a Database
  • Displaying Log Entries in an Admin Panel

Once we know what Pagination is, let’s jump into how you can implement pagination in your C# projects!

Implementing Pagination in C#

Let’s start with the set up of Pagination in C#. We’ll discuss basics like the Skip and Take methods, using LINQ, ASP.NET Core, and more. Strap in!

Basic Pagination with Skip and Take

To paginate through data in C#, you can use the Skip and Take methods together. Here’s a simple example:

var pageNumber = 1;
var pageSize = 10;var paginatedData = dataArray    .Skip((pageNumber - 1) * pageSize)    .Take(pageSize)    .ToList();

In this snippet:

  • pageNumber: The number of the page you want to display.
  • pageSize: How many items to show per page.
  • Skip: Skips over a specified number of elements.
  • Take: Takes the specified number of elements after the skip.

Using LINQ for Efficient Pagination

LINQ (Language-Integrated Query) is a powerful tool in C#. Pagination with LINQ is easy and very readable:

var pageNumber = 2;
var pageSize = 5;var pagedQuery = from item in dataArray                 orderby item.SomeProperty                 select item;var paginatedResult = pagedQuery    .Skip((pageNumber - 1) * pageSize)    .Take(pageSize)    .ToList();

This example orders the items by SomeProperty before applying Skip and Take.

Pagination in ASP.NET Core

ASP.NET Core makes pagination even simpler. You can create a paginated response in your controller:

[HttpGet]
public IActionResult GetPagedData(int pageNumber = 1, int pageSize = 10){    var data = _context.DataSet        .Skip((pageNumber - 1) * pageSize)        .Take(pageSize)        .ToList();    return Ok(data);}

And just like that, your API endpoint is ready to serve paginated data.

Handling Pagination with Entity Framework Core

Entity Framework Core (EF Core) supports pagination out of the box. Here’s an example of paginating data with EF Core:

public async Task<IActionResult> GetPagedData(int pageNumber = 1, int pageSize = 10)
{    var data = await _context.DataEntities        .OrderBy(d => d.SomeProperty)        .Skip((pageNumber - 1) * pageSize)        .Take(pageSize)        .ToListAsync();    return Ok(data);}

Using ToListAsync ensures the paginated data retrieval is efficient and non-blocking.

Pagination for Large Data Sets

When dealing with large data sets, you need to be mindful of performance. Here’s a quick tip: always index your database columns you’re sorting by. This can significantly speed up queries.

Advanced Pagination Techniques

Alright, let’s dive into some advanced concepts. Custom logic, server/client-side pagination, and even infinite scroll.

Custom Pagination Logic

Custom pagination might be needed for more control. For instance, if you want unique page numbering, sorting, or filtering:

public PagedResult<T> GetPagedData<T>(IQueryable<T> query, int page, int pageSize) where T : class
{    var result = new PagedResult<T>    {        CurrentPage = page,        PageSize = pageSize,        RowCount = query.Count()    };    var pageCount = (double)result.RowCount / pageSize;    result.PageCount = (int)Math.Ceiling(pageCount);    var skip = (page - 1) * pageSize;    result.Results = query.Skip(skip).Take(pageSize).ToList();    return result;}

Here, PagedResult<T> is a helper class encapsulating paginated data and meta-information like CurrentPage, PageSize, RowCount, etc.

Server-Side vs. Client-Side Pagination

Let’s break it down:

  • Server-Side Pagination: Data is fetched page by page from the server. Suitable for huge datasets.
  • Client-Side Pagination: All data is loaded upfront, and pagination controls which part is visible. Good for small datasets to minimize server calls.

Performance Considerations for Pagination in C#

To make your pagination efficient:

  • Use database indexing.
  • Avoid fetching unnecessary data.
  • Implement caching if data doesn’t change frequently.

Infinite Scroll Pagination

Ever been on Instagram or Twitter? That’s infinite scroll pagination. Here’s how you could do it in C#:

public async Task<IActionResult> GetInfiniteScrollData(int lastItemId, int pageSize = 10)
{    var data = await _context.DataEntities        .Where(d => d.Id > lastItemId)        .OrderBy(d => d.Id)        .Take(pageSize)        .ToListAsync();    return Ok(data);}

In this example, lastItemId represents the ID of the last loaded item. Sweet, right?

Pagination in API Design

Designing APIs that support pagination can significantly enhance your application’s performance and usability. Stick around, we’re about to explore how to do this seamlessly.

Designing Paginated API Endpoints

When designing APIs, you can include parameters like page and size to handle pagination:

GET /api/data?page=1&size=10

Implementing Pagination in ASP.NET Web API

Here’s a practical implementation in ASP.NET Web API:

[HttpGet]
public async Task<IActionResult> GetPagedData(int pageNumber = 1, int pageSize = 10){    var data = await _context.DataEntities        .OrderBy(d => d.SomeProperty)        .Skip((pageNumber - 1) * pageSize)        .Take(pageSize)        .ToListAsync();    var totalRecords = await _context.DataEntities.CountAsync();    var pagedData = new {        TotalRecords = totalRecords,        Data = data    };    return Ok(pagedData);}

This response includes both paginated data and the total record count, ensuring the client knows how many pages there are.

Working with Page Sizes and Limits in APIs

It’s a good idea to include logic to handle page size limits, preventing requests for excessively large pages:

int MaxPageSize = 50;
pageSize = (pageSize > MaxPageSize) ? MaxPageSize : pageSize;

User Interface and UX Considerations

Pagination isn’t just about the backend. It plays a huge role in the front-end UX. Let’s talk about making pagination user-friendly and seamless!

Designing User-Friendly Pagination Controls

Here are some tips:

  • Use understandable navigation controls like “First”, “Previous”, “Next”, and “Last”.
  • Display current page and total page numbers.
  • Offer page size options (e.g., 10, 20, 50 items per page).

Adding Pagination to Razor Pages

In Razor Pages, adding pagination is straightforward. Here’s an example of a paginated view:

@page
@model PagedListModel<table>    <thead>        <tr>            <th>Item</th>        </tr>    </thead>    <tbody>        @foreach (var item in Model.Items)        {            <tr>                <td>@item.Name</td>            </tr>        }    </tbody></table><nav aria-label="Page navigation">    <ul class="pagination">        <li class="page-item">            <a class="page-link" href="?page=@(Model.CurrentPage - 1)">Previous</a>        </li>        @for (int i = 1; i <= Model.TotalPages; i++)        {            <li class="page-item">                <a class="page-link" href="?page=@i">@i</a>            </li>        }        <li class="page-item">            <a class="page-link" href="?page=@(Model.CurrentPage + 1)">Next</a>        </li>    </ul></nav>

Integrating Pagination with JavaScript and AJAX

Using JavaScript and AJAX, you can make page transitions smoother:

$(document).on('click', '.page-link', function(e) {
    e.preventDefault();    var page = $(this).attr('href').split('page=')[1];    $.ajax({        url: '/api/data?page=' + page,        type: 'GET',        success: function(data) {            // Update the table with new data        }    });});

This snippet hooks onto the pagination links, captures the click event, and fetches new data using AJAX.

Testing and Debugging Pagination

Even the best code can run into issues. Let’s make sure our pagination logic is robust and well-tested.

Unit Testing Pagination Logic

Unit tests ensure your pagination logic performs as expected:

[TestMethod]
public void TestPagination(){    var data = Enumerable.Range(1, 100).ToList();    var pagedData = Paginate(data, 2, 10);    Assert.AreEqual(10, pagedData.Count());    Assert.AreEqual(11, pagedData.First());    Assert.AreEqual(20, pagedData.Last());}

Common Issues and Troubleshooting

Common pagination issues include:

  • Incorrect page number calculations.
  • Off-by-one errors.
  • Performance bottlenecks with large datasets.

Performance Testing Pagination

Use tools like JMeter or Postman for performance testing your paginated endpoints. Ensure they handle concurrent users efficiently.

Best Practices and Tips for Effective Pagination

Let’s wrap up with some essential best practices and tips to make your pagination top-notch.

SEO Considerations for Paginated Content

Ensure each paginated page is crawlable by search engines:

  • Use rel="next" and rel="prev" links in the HTML <head>.
  • Include canonical tags to avoid duplicate content issues.

Efficient Database Queries for Pagination

Using indexed queries and TOP for efficient data retrieval:

var result = context.DataEntities
    .OrderBy(d => d.SomeProperty)    .Skip((pageNumber - 1) * pageSize)    .Take(pageSize)    .ToList();

Using Caching and Indexing for Improved Performance

Caching repeated queries and indexing frequently searched columns can drastically improve performance:

  • Utilize MemoryCache or Redis for caching.
  • Index database columns used in OrderBy, Where, and Join clauses.

Conclusion

We’ve covered the basics of pagination, explored advanced techniques, and discussed API design and UX considerations. By now, you should be well-equipped to implement, optimize, and troubleshoot pagination in your C# applications.

Remember, effective pagination not only enhances performance but also greatly improves user experience. Happy coding!

--

--

Juan España
ByteHide

CEO at ByteHide🔐, passionate about highly scalable technology businesses and .NET content creator 👨‍💻