Dynamic Sorting with Linq

When trying to implement a Business Logic Layer (i will refer to this as BLL) with linq one thing that is an annoyance is sorting. Lets say you had a BLL that was being used by ObjectDataSource and had a select method that does sorting and paging, then you'd probably be tempted to write something like this:

private IQueryable<Product> SortBy(IQueryable<Product> source, string sortBy) {

    int desc = sortBy.IndexOf("DESC");

    bool isDescending = desc >= 0;

    if (isDescending) {

        sortBy = sortBy.Substring(0, desc).Trim();

    }

    switch (sortBy) {

        case "ProductName":

            if (isDescending) {

                return source.OrderByDescending(p => p.ProductName);

            }

            source = source.OrderBy(p => p.ProductName);

            break;

        case "UnitPrice":

            if (isDescending) {

                return source.OrderByDescending(p => p.UnitPrice);

            }

            source = source.OrderBy(p => p.UnitPrice);

            break;

..... One for every property

    }

    return source;

}

But that can be really tedious and you probably have more than one object. Would you want to duplicate that for Categories, Suppliers, etc? I don't think so. Linq takes
advantage of another C# 3.0 feature called Expression Trees and we can take advantage of these in our code to build a dynamic sort expression for any object.

The code:
So how do we do this? I'm not going to explain how expression trees work in this blog post but there are plenty of resources out there  that you can take a look at if your interested.
The method is an extension method on IQueryable<T> that takes the IQuerable<T> source and the sort parameter which should be the property name that you would like to sort by. In the case of DataSources like ObjectDataSource, the sort parameter will contain "DESC' if the sort direction is descending. Here is the code:

public static class QueryExtensions {

    public static IQueryable<T> SortBy<T>(this IQueryable<T> source, string propertyName) {

        if (source == null) {

            throw new ArgumentNullException("source");

        }

        // DataSource control passes the sort parameter with a direction

        // if the direction is descending          

        int descIndex = propertyName.IndexOf(" DESC");

        if (descIndex >= 0) {

            propertyName = propertyName.Substring(0, descIndex).Trim();

        }

 

        if (String.IsNullOrEmpty(propertyName)) {

            return source;

        }

 

        ParameterExpression parameter = Expression.Parameter(source.ElementType, String.Empty);

        MemberExpression property = Expression.Property(parameter, propertyName);

        LambdaExpression lambda = Expression.Lambda(property, parameter);

 

        string methodName = (descIndex < 0) ? "OrderBy" : "OrderByDescending";

 

        Expression methodCallExpression = Expression.Call(typeof(Queryable), methodName,

                                            new Type[] { source.ElementType, property.Type },

                                            source.Expression, Expression.Quote(lambda));

 

        return source.Provider.CreateQuery<T>(methodCallExpression);

    }

}

And a link to the file.

QueryExtensions.cs

Hope this helps

9 Comments

Comments have been disabled for this content.