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:
- It can require a lot of code, especially if I also want to allow the user to specify the sort direction too.
- 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.
- 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.
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:
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.