Bind List Using LINQ Extension

Extending System.Linq.IOrderedEnumerable

Recently I was writing code to develop a web page that displayed a list of users. The page also had a drop down list containing the following sort options:

  • Sort by last Name Descending
  • Sort by last Name ascending
  • Sort by Age descending
  • Sort by Age ascending
  • Sort by Activity Date desc
  • Sort by Activity Date asc
  • Sort by Login Date desc

The users have to be grouped by location by default and then the selected sort was to be applied within each group.

The tricky business requirement was that if there is a tie while sorting by age or Last Name, the tied users had to be further sorted by Activity Date descending and if there was still a tie, the users had to be sorted by Login Date descending.

Assume that the user decided to sort the default list using one of the options above. Let us call this variable "UserSort".

  • if the UserSort was "LastNameDesc" then we need to sort by the DB field called LastName.
  • if the UserSort was "LastNameDesc" then we need to sort by the DB field called LastName in descending order
  • if the UserSort was "Age" then we need to sort by the DB field called Age

So we need to determine the DB field to sort by, based on the user selection. We cannot do that using the existing OrderBy methods on IEnumerable as shown below, since we do not know what field we will be sorting by.

IEnumerable lstUsers = GetUsers();
lstUsers = lstUsers.OrderBy(x=>x.Location)
//default ordering ThenOrderBy(WHAT FIELD DO WE SPECIFY HERE?)

This is where Extension methods are useful. The following is the extension method on IOrderedEnumerable that enables us to add the required order to the list based on the user's selected sort option.

public static IOrderedEnumerable ThenByCustomSort(this IOrderedEnumerable defaultList,string sortOrder)
{
    switch(sortOrder)
    {
        case "LastNameDesc":
            return defaultList.ThenByDescending(x=>x.LastName);
        case "LastNameAsc":
            return defaultList.ThenBy(x=>x.LastName);
        case "AgeDesc":
            return defaultList.ThenByDescending(x=>x.Age);
        case "AgeAsc":
            return defaultList.ThenBy(x=>x.Age);
        case "ActivityDateDesc":
            return defaultList.ThenByDescending(x=>x.ActivityDate);
        case "ActivityDateAsc":
            return defaultList.ThenBy(x=>x.ActivityDate);
        case "LoginDateDesc":
            return defaultList.ThenByDescending(x=>x.LoginDate);
        default:
            return defaultList;
    } 
}


This is how we call this method:

IEnumerable lstUsers = GetUsers();
lstUsers = lstUsers.OrderBy(x => x.Location)
.ThenByCustomSort(UserSort);


Now for the tie in the results. We do something similar and create an extension method as in the following:

public static IOrderedEnumerable ThenByTieBreakSort(this IOrderedEnumerable defaultList, string sortOrder)
{
    switch(sortOrder)
    {
        case "LastNameDesc":
        case "LastNameAsc":
        case "AgeDesc":
        case "AgeAsc":
            return defaultList.ThenByCustomSort("ActivityDateDesc").ThenByCustomSort("LoginDateDesc");
        case "ActivityDateDesc":
            return defaultList.ThenByCustomSort("LoginDateDesc");
        default:
            return defaultList;
    }
}

After adding the tie-breaking code, the code now looks as in the following:

IEnumerable lstUsers = GetUsers();
lstUsers = lstUsers.OrderBy(x => x.Location)
.ThenByCustomSort(UserSort)
.ThenByTieBreakSort(UserSort);

Important

Since LINQ queries are executed using deferred execution, we need to ensure that we are adding additional sort options well before the query is executed, which is exactly what the use of the extension methods above does.

Note: I have used a string data type for the sortOrder just for the purposes of this example. It is better to use a strongly typed data type, like enums, for the purpose. I have also not taken string casing into consideration.


Similar Articles