Monthly Archives

2 Articles

Visual Studio Tips

Tutorial – Paging in .NET Core MVC Part 1

Posted by matteskolin on

github for code used in this tutorial –

In this tutorial, we will assemble a class library that can be used to generate html markup to support paging. The UI should end up looking something like this.

The front end client code to use the library should be simple and easy to use. We are using Visual Studio Code with .NET 5, so I will implement the final result as a tag helper and possibly a view component, so we can write code like this to activate paging.

<h3>My Products</h3>
<!-- Html tag helper will be triggered by the mvc-pager-info attribute being present -->
<div mvc-pager-info="@ViewBag.pagingInfo"></div>

We need the ability to configure the number of results per page, the styling of the buttons, and the presence of next and previous buttons. The pager should be fast and not cause the database to return huge datasets beyond what is needed to display a single page.

Gathering Code Examples and Creating the Project

Instead of writing this functionality from scratch, we will use the below github repositories as sources, and combine them to something that melds nicely into a .NET 5 MVC project, however, much of this functionality can be used outside the context of MVC such as in razor pages or a console application.

https://github.com/hieudole/PagedList.Core.Mvc – ideas for the tag helper
https://github.com/troygoode/PagedList – sql paging implementation – calculating the metadata such as total count, number of pages, and querying the database.

Neither of the above projects has had much much maintenance or updates as of this writing. I hope to build something that works for .NET 5.

Creating the Project

Use the .NET CLI to create a new project with the MVC template. Run the below command in your powershell terminal of choice. The VS Code terminal works well for this. This creates a project structure with a .csproj file, and a basic template page with supporting files in the views, models, controllers folders.

dotnet new mvc --name MvcPaging

Random Test Data for Paging

In Order to build a pager, we first must have a test dataset that is big enough to require paging. I decided to use a list of randomly generated strings, each five characters long. This is a static class so that we can generate the data only once during our program execution, and not on every http request.

using System.Collections.Generic;

namespace DotNetPaging.Examples.Models
{
    public static class DataSet{

        static System.Random random = new System.Random();
        public static List<string> StringDataSet {get; set;} = new List<string>();

        public static void CreateDataSet(){

            for(int i = 0; i< 100;i++){

                var randomLetters = $"{GetLetter()}{GetLetter()}{GetLetter()}{GetLetter()}{GetLetter()}{GetLetter()}";
                StringDataSet.Add(randomLetters);
            }
        }

        public static string GetLetter()
        {
            // This method returns a random lowercase letter
            // ... Between 'a' and 'z' inclusize.
            int num = random.Next(0, 26); // Zero to 25
            char let = (char)('a' + num);
            return let.ToString();
        }
    }
}

Now that we have 100 items of data to display, we can display this data on a page and add the pager so only a subset of the results is displayed at once.

Add The Model – Introduce PagedList<T>

The Model will be very simple. It’s purpose is to pass data from the MVC Controller which we will add below, to the View. Here our Model will contain a single property of type PagedList<string>. The PagedList<T> type is inherited from IEnumerable<T>.

namespace DotNetPaging.Examples.Models{
    public class HomeIndex_VM {
        public IPagedList<string> OnePageOfData {get; set;}
    }
}

The implementation adds metadata to describe the data, including

PageCount
TotalItemCount
PageNumber
PageSize
HasPreviousPage
HasNextPage
IsFirstPage
IsLastPage
FirstItemOnPage
LastItemOnPage

These properties are all calculated based on the total count of items in the database, the current page being viewed, and the number of items per page. Since I am using an existing paging library for calculating this metadata, I won’t discuss much here how the metadata is being calculated but you can see the details in the PagedList.cs file linked in the github repo at the top of this post.

Next we will add the controller and the view

Adding the Controller and View

        PageCount
        TotalItemCount 
        PageNumber 
        PageSize 
        HasPreviousPage
        HasNextPage
        IsFirstPage
        IsLastPage
        FirstItemOnPage 
        LastItemOnPage 

Adding the Controller And the View

//HomeController.cs
using Microsoft.AspNetCore.Mvc;
using DotNetPaging.Examples.Models;

namespace DotNetPaging.Examples.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index([FromQuery]int? page)
        {
            var pageNumber = page == null || page < 0 ? 1 : page.Value;
            var pageSize = 5;

            var onePage = DataSet.StringDataSet.ToPagedList(pageNumber,pageSize);

           var indexViewModel = new HomeIndex_VM(); 
           indexViewModel.OnePageOfData = onePage;

            return View(indexViewModel);
        }
    }
}

In Our view we are using the pager tag helper.

@using DotNetPaging;
@model DotNetPaging.Examples.Models.HomeIndex_VM
@{
    ViewData["Title"] = "DotNetPaging - Examples";
}

<h1>Paging</h1>
<p><h6>Full Pager</h6><pager class="pager-container" list="@Model.OnePageOfData" options="@PagedListRenderOptions.Bootstrap4Full" asp-action="Index" asp-controller="Home"  /></p>

<table class="table">
    @foreach(var item in @Model.OnePageOfData)
    {
        <tr>
            <td>
                @item
            </td>
        </tr>
    }
</table>

The pager tag helper will build something like the below html output. The configuration of the pagedlist and the paged list meta data which has been previously calculated will determine how this html is constructed. The tag helper will generate the appropriate number of links, along with the next , previous, last, and first buttons.

<nav class="pager-container">
   <ul class="pagination">
      <li class="page-item disabled"><a class="page-link" tabindex="-1">First</a></li>
      <li class="page-item disabled"><a class="page-link" rel="prev" tabindex="-1">Previous</a></li>
      <li class="page-item active"><span class="page-link">1</span></li>
      <li class="page-item"><a class="page-link" href="/?page=2">2</a></li>
      <li class="page-item"><a class="page-link" href="/?page=3">3</a></li>
      <li class="page-item"><a class="page-link" href="/?page=2" rel="next">Next</a></li>
      <li class="page-item"><a class="page-link" href="/?page=20">Last</a></li>
   </ul>
</nav>

Code available on github





Part 2 will be adding a randomize button to the




Will add to this post as I continue building this project.

More to Come!

Maybe we should consider building this as a View Component instead of a tag helper… to be determined

Really want to add random – ordering to the paging so that we can see fresh items.. that are burried in the middle of the list…