Here's something that might help you. This will give something you can use for dynamic sorting in other projects as well.
The class for containing sorting info:
public class SortRequest
{
/// <summary>
/// What field/column to sort by
/// </summary>
public string Field { get; set; }
/// <summary>
/// Whether to sort by descending or not.
/// Defaults to false
/// </summary>
public bool SortDescending { get; set; } = false;
}//Cls
The class for doing the sorting
public static class SortingExtensions
{
//------------------------------------------------------------------------//
/// <summary>
/// Sorts <paramref name="list"/> according to the details in <paramref name="sortRequestList"/>
/// </summary>
/// <typeparam name="T">The type of objects being sorted</typeparam>
/// <param name="list">The items to sort</param>
/// <param name="sortRequestList">List of fields to sort by</param>
/// <param name="getPropertySelectorLambda">Function to convert string field to a T property</param>
/// <returns>The same list with sorting appended to it</returns>
public static IEnumerable<T> AddSorting<T>(this IEnumerable<T> list, IEnumerable<SortRequest> sortRequestList, Func<string, Func<T, object>> getPropertySelectorLambda)
{
if (sortRequestList == null || !sortRequestList.Any() || list == null || !list.Any())
return list;
var isFirst = true;
foreach (var sr in sortRequestList)
{
Func<T, object> lmda = getPropertySelectorLambda(sr.Field);
list = list.AddSortLevel(sr.SortDescending, lmda, isFirst);
isFirst = false;
}//foreach
return list;
}//SortProducts
//------------------------------------------------------------------------//
/// <summary>
/// Adds a level of sorting to the linq queue
/// </summary>
/// <typeparam name="T">Type of objects being sorted</typeparam>
/// <param name="list">The Items being sorted</param>
/// <param name="sortDescending">What direction to sort</param>
/// <param name="propertySelectorLambda">Function dexcribing what property to sort on.</param>
/// <param name="isFirst">Is this the first item in the LINQ sort queue</param>
/// <returns>The same list with sorting appended to it</returns>
private static IOrderedEnumerable<T> AddSortLevel<T>(this IEnumerable<T> list, bool sortDescending, Func<T, object> propertySelectorLambda, bool isFirst)
{
if (isFirst)
list = FirstSortLevel(list, sortDescending, propertySelectorLambda);
else
list = NextSortLevel(list as IOrderedEnumerable<T>, sortDescending, propertySelectorLambda);
//Will definitely be IOrderedEnumerable here.
return list as IOrderedEnumerable<T>;
}//FirstSortLevel
//------------------------------------------------------------------------//
/// <summary>
/// Adds a level of sorting to the linq queue (OrderBy)
/// </summary>
/// <typeparam name="T">Type of objects being sorted</typeparam>
/// <param name="list">The Items being sorted</param>
/// <param name="sortDescending">What direction to sort</param>
/// <param name="propertySelectorLambda">Function dexcribing what property to sort on.</param>
/// <param name="isFirst">Is this the first item in the LINQ sort queue</param>
/// <returns>The same list with sorting appended to it</returns>
private static IOrderedEnumerable<T> FirstSortLevel<T>(this IEnumerable<T> list, bool sortDescending, Func<T, object> propertySelectorLambda)
{
if (sortDescending)
list = list.OrderByDescending(propertySelectorLambda);
else
list = list.OrderBy(propertySelectorLambda);
//Will definitely be IOrderedEnumerable here.
return list as IOrderedEnumerable<T>;
}//FirstSortLevel
//------------------------------------------------------------------------//
/// <summary>
/// Adds a level of sorting to the linq queue (ThenBy)
/// </summary>
/// <typeparam name="T">Type of objects being sorted</typeparam>
/// <param name="list">The Items being sorted</param>
/// <param name="sortDescending">What direction to sort</param>
/// <param name="propertySelectorLambda">Function dexcribing what property to sort on.</param>
/// <param name="isFirst">Is this the first item in the LINQ sort queue</param>
/// <returns>The same list with sorting appended to it</returns>
private static IOrderedEnumerable<T> NextSortLevel<T>(this IOrderedEnumerable<T> list, bool sortDescending, Func<T, object> propertySelectorLambda)
{
if (sortDescending)
list = list.ThenByDescending(propertySelectorLambda);
else
list = list.ThenBy(propertySelectorLambda);
return list;
}//NextSortLevel
//------------------------------------------------------------------------//
}//Cls
How to use it:
YOu pass in a list of sortRequests, each one will have the name of your property and the direction to sort.
Then the getPropertySelectorLambda will give you the Expression/Function used in the sorting.
Here's an example of something I use:
private Func<Nurse, object> GetSortingPropertySelectorLambda(string field)
{
return (field.CamelToPascal()) switch
{
nameof(Nurse.Id) => p => p.Id,
nameof(Nurse.PinNumber) => p => p.PinNumber,
nameof(Nurse.FirstName) => p => p.FirstName,
nameof(Nurse.LastName) => p => p.LastName,
nameof(Nurse.AddressLine1) => p => p.AddressLine1,
nameof(Nurse.AddressLine2) => p => p.AddressLine2,
nameof(Nurse.AddressLine3) => p => p.AddressLine3,
nameof(Nurse.AddressLine4) => p => p.AddressLine4,
nameof(Nurse.PhoneNumber) => p => p.PhoneNumber,
nameof(Nurse.PhoneNumber2) => p => p.PhoneNumber2,
nameof(Nurse.EmailAddress) => p => p.EmailAddress,
nameof(Nurse.DateOfBirth) => p => p.DateOfBirth,
nameof(Nurse.PayrollNumber) => p => p.PayrollNumber,
nameof(Nurse.Branch) => p => p.Branch.Name,
_ => p => p.Id,
};
}//GetSortingPropertySelectorLambda
YOu could alternatively build that expression yourself from the sortRequest (Like you've done above).
The reason we have the "FirstSortLevel" method is because the first sort must be OrderBy but all the following sorts must be "ThenBy"s.
So all we're doing is creating/getting the expression which decides what property to filter on. Then just use regular LINQ to so the sorting (We don't build those queries as expressions).
Example Usage:
var sortedData = allData
.AddSorting(sortList, GetSortingPropertySelectorLambda)
.ToList();