Adding Sorting To Paging In ASP.NET Core Razor Pages

This article builds on the simple paging functionality that was added to a Razor Pages application in my last article by showing how to add sorting functionality so that the user can determine the order in which results are displayed.

Most sorting examples show how to pass the column header to C# code that uses a switch statement to build a LINQ query:

var query = context.SomeData;
switch(columnHeader)
{ 
    case "ID":
        query = query.SortBy(c => c.ID);
        break;
    case "FullName":
        query = query.SortBy(c => c.FullName);
        break;
    // etc
}    

This works but it suffers from 3 limitations:

  1. It can require a lot of code, especially if I also want to allow the user to specify the sort direction too.
  2. It is not reusable. I can't use this code block with a different data set because it is dependent on the columns in the result set.
  3. It is not particularly scalable. If I want to add or remove columns, I have to modify the switch statement which could result in the introduction of bugs.

A really neat solution to this is the Dynamic Linq library. It provides extension methods such as OrderBy that take string arguments instead of type-safe expressions. It means that I don't have to know the shape of the model that the OrderBy method acts upon at design time. It is evaluated dynamically at runtime. This was originally released by Microsoft, but not as part of the .NET Framework. It has since been ported to .NET Core by a few people, one example of which can be found here: https://github.com/StefH/System.Linq.Dynamic.Core. I can install it using Nuget:

install-package System.Linq.Dynamic.Core

Or I can use the command line:

dotnet add package System.Linq.Dynamic.Core

Once it is available to the application, I add another method to the PersonService from the previous article. Here it is again in its entirety with its interface:

The addition is the version of the GetPaginatedResult method that takes a string representing the column to sort by. I need to change the OnGetAsync method in the PageModel to call this method, and I need to add a property to the PageModel to represent the column to sort by. Here is the revised PageModel class:

One other change to note - I removed the default value for the CurrentPage property. I am going to move that to the page's route template instead, and add another parameter for sortby:

@page "{currentpage=1}/{sortby=Id}"

The only thing left to do now is to add column headers to the table with hyperlinks to specify the sort order, and to adjust the paging links. Here is the revised content page:

The thead element contains the column headers with the links. The links themselves are generated by anchor tag helpers. Each one has an asp-route-sortby attribute set to the value of the respective column. This will ensure that the correct value is provded to the RouteData dictionary, from where it will be bound to the SortBy property in the PageModel. The value for the currentpage parameter will be supplied from ambient route values.

Paging and Sorting in Razor Pages

The revised paging links at the bottom of the code include values for both the currentpage and the sortby parameters. The currentpage value is set explicitly, which has the effect of negating all subsequent ambient route values, so the sortby value must also be set explicitly.

Now when you click one of the sorting links, the paging links also include the sorting information:

Paging and Sorting in Razor Pages

Summary

A lot of people automatically reach for third party components when they want to add paging and sorting to a table of data, but this article and the one before it show that implementing these features is not complicated at all. Dynamic Linq makes adding generic sorting functionality a relatively pain-free process.