Click here to Skip to main content
Licence CPOL
First Posted 28 Aug 2011
Views 9,730
Downloads 105
Bookmarked 11 times

Handle GridView.OnSorting() and create sorting expression dynamically using LINQ

By | 22 Mar 2012 | Article
How to create a sorting expression from GridViewSortEventArgs using LINQ Expression Tree.

Introduction

The GridView control from ASP.NET 2.0 is great and widely used but has a significant limitation: the sorting event argument GridViewSortEventArg is string-based and is based on values in markup. So it can't be directly used in programmatic sorting using LINQ.

<asp:GridView runat="server" ID="gridPersons" 
         AutoGenerateColumns="false" AllowSorting="true" 
         OnSorting="gridPersons_Sorting">
    <Columns>
        <asp:BoundField HeaderText="First name" 
           DataField="FirstName" 
           SortExpression="FirstName" />
        <asp:BoundField HeaderText="Last name" 
           DataField="LastName" 
           SortExpression="LastName" />
    </Columns>
</asp:GridView>
protected void gridPersons_Sorting(object sender, GridViewSortEventArgs e)
{
    string sortExpression = e.SortExpression;
}

This article describes one of the methods to convert a string argument into a sorting expression.

Background

Let's assume we have a sequence of strongly typed class, say Person, and we want to bind it to a GridView and then sort it by clicking on the appropriate column:

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

IEnumerable<Person> persons = GetPersons();
gridPersons.DataSource =  persons.ToArray();
gridPersons.DataBind();

Generally speaking, we could hard-code both the sorting expression (i.e., property to sort by) and the direction (ascending, descending):

protected void gridPersons_Sorting(object sender, GridViewSortEventArgs e)
{
    Func<Person, object> prop = null;
    switch (e.SortExpression)
    {
        case "FirstName":
        {
            prop = p => p.FirstName;
            break;
        }
        case "LastName":
        {
            prop = p => p.LastName;
            break;
        }
    }

    Func<IEnumerable<Person>, Func<Person, object>, 
           IEnumerable<Person>> func = null;
    switch (e.SortDirection)
    {
        case SortDirection.Ascending:
        {
            func = (c, p) => c.OrderBy(p);
            break;
        }
        case SortDirection.Descending:
        {
            func = (c, p) => c.OrderByDescending(p);
            break;
        }
    }

    IEnumerable<Person> persons = GetPersons();
    persons = func(persons, prop);

    gridPersons.DataSource = persons.ToArray();
    gridPersons.DataBind();
}

But such an approach contains a number of disadvantages: all the object's properties are hard-coded, as well as the sorting directions. Much better would be to create all that dynamically.

The Code 

First let's wrap the sorting directions into classes and a dedicated helper class:

public static class SortExpressionBuilder<T>
{
    private static IDictionary<SortDirection, ISortExpression> directions = 
            new Dictionary<SortDirection, ISortExpression>
    {
        { SortDirection.Ascending, new OrderByAscendingSortExpression() },
        { SortDirection.Descending, new OrderByDescendingSortExpression() }
    };

    interface ISortExpression
    {
        Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> GetExpression();
    }

    class OrderByAscendingSortExpression : ISortExpression
    {
        public Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> GetExpression()
        {
            return (c, f) => c.OrderBy(f);
        }
    }

    class OrderByDescendingSortExpression : ISortExpression
    {
        public Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> GetExpression()
        {
            return (c, f) => c.OrderByDescending(f);
        }
    }

    public static Func<IEnumerable<T>, Func<T, object>, 
      IEnumerable<T>> CreateExpression(SortDirection direction)
    {
        return directions[direction].GetExpression();
    }
}

And wrap property selector created dynamically using LINQ Expression Tree into a handy extension method:

public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> collection, 
       string columnName, SortDirection direction)
{
    ParameterExpression param = Expression.Parameter(typeof(T), "x"); // x
    Expression property = Expression.Property(param, columnName);     // x.ColumnName
    Func<T, object> func = Expression.Lambda<Func<T, object>>(        // x => x.ColumnName
        Expression.Convert(Expression.Property(param, columnName), 
        typeof(object)), param).Compile();

    Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> expression =
        SortExpressionBuilder<T>.CreateExpression(direction);
    IEnumerable<T> sorted = expression(collection, func);
    return sorted;
}

So now we have a flexible, fully dynamic and generic solution:

protected void gridPersons_Sorting(object sender, GridViewSortEventArgs e)
{
    IEnumerable<Person> persons = GetPersons();
    persons = persons.OrderBy(e.SortExpression, e.SortDirection);

    gridPersons.DataSource = persons.ToList();
    gridPersons.DataBind();
}

Points of Interest 

Here are some discussions on StackOverflow related to the subject:

History 

  • 27/08/2011 - Version 1.0 - The initial release.
  • 22/03/2012 - Version 1.0.1 - OrderBy<T>() extension method expanded to make it more easy to understand

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Alexander M. Batishchev

Software Developer
Mary Kay Russia
Russian Federation Russian Federation

Member

Follow on Twitter Follow on Twitter
Senior .NET developer, novice software architect.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionDynamic.cs Pinmembertmbgfan6:32 22 Mar '12  
AnswerRe: Dynamic.cs PinmemberAlexander M. Batishchev7:11 22 Mar '12  
GeneralRe: Dynamic.cs Pinmembertmbgfan8:16 22 Mar '12  
QuestionExplaination PinmemberKnvnBhat19:52 21 Mar '12  
AnswerRe: Explaination PinmemberAlexander M. Batishchev21:53 21 Mar '12  
QuestionIt does not work properly... Pinmemberjulgon20028:23 20 Mar '12  
QuestionRe: It does not work properly... PinmemberAlexander M. Batishchev5:22 21 Mar '12  
SuggestionNeeded changes PinmemberLeoric22:27 29 Aug '11  
GeneralRe: Needed changes PinmemberAlexander M. Batishchev21:55 21 Mar '12  
QuestionGood code but no explanation :( PinmemberDavid Zenou8:02 28 Aug '11  
AnswerRe: Good code but no explanation :( PinmemberAlexander M. Batishchev8:11 28 Aug '11  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web03 | 2.5.120528.1 | Last Updated 22 Mar 2012
Article Copyright 2011 by Alexander M. Batishchev
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid