1

I have a grid on a page which I want to be sortable on multiple columns at the same time.

For example:

UserID        FirstName        LastName
=======================================
1             Bruce            Wayne
2             Peter            Parker
3             Clark            Kent
4             Tony             Stark
5             Helena           Wayne

The user can choose to order by LastName ASC then by FirstName DESC, which would produce the following:

UserID        FirstName        LastName
=======================================
3             Clark            Kent
2             Peter            Parker
4             Tony             Stark
5             Helena           Wayne
1             Bruce            Wayne

The user could reset the ordering and decide to order it in some other fashion.

How can I achieve this in LINQ? As far as I know, the way to chain ordering is to do something like

superheroes.OrderBy(x => x.LastName).ThenByDescending(x => x.FirstName)

Obviously, I don't want to have to write out every possible combination of column orders (my grid may have up to 10 columns). Is there some way to make the ordering sequence dynamic?

Andrew
  • 11,068
  • 17
  • 52
  • 62

3 Answers3

3

A naive approach for IEnumerable (not perfect or tested, but you get the idea). I've put the switch on the outside so the comparison is done only once, not for each time the selector is called. A little ugly because you essentially have to repeat yourself in OrderBy and ThenBy, though.

enum OrderableColumns {UserID, FirstName, ...}

IOrderedEnumerable<SuperHero> OrderBy(SuperHeroes superheroes, OrderableColumns column)
{
    switch(column)
    {
        case UserID:
            return superheroes.OrderBy(x => x.UserID);
        case FirstName:
            return superheroes.OrderBy(x => x.FirstName);
        ...
    }
}

IOrderedEnumerable<SuperHero> ThenBy(IOrderedEnumerable<SuperHero> superheroes, OrderableColumns column)
{
    switch(column)
    {
        case UserID:
            return superheroes.ThenBy(x => x.UserID);
        case FirstName:
            return superheroes.ThenBy(x => x.FirstName);
        ...
    }
}

IOrderedEnumerable<SuperHero> OrderSuperheroes(SuperHeroes superheroes, params OrderableColumns[] columns)
{
     var ordered = OrderBy(superheroes, columns[0]);

     foreach(var col in columns.Skip(1))
         ordered = ThenBy(ordered, col);

     return ordered;
}
lc.
  • 113,939
  • 20
  • 158
  • 187
2

You need to use dynamic LINQ. Have a look at Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library)

KV Prajapati
  • 93,659
  • 19
  • 148
  • 186
  • For reference, I ended up using the dynamic linq helper extensions methods here: http://stackoverflow.com/questions/41244/dynamic-linq-orderby – Andrew Aug 29 '12 at 02:05
0

You can build an Func<T,TResult> Dynamic, like this:

public static Func<T,object> GetExp<T>(string preportyName)
{
    var instance = Expression.Parameter(typeof(T));
    var callPreporty = Expression.PropertyOrField(instance, preportyName);
    var lambda = Expression.Lambda<Func<T, object>>(callPreporty,instance);
    return lambda.Compile();
}

Use like this:

var pdotName = GetExp<Person>("Name");  //equals: p=>p.Name
var pdotID = GetExp<Person>("Id");  ////equals: p=>p.Id
var ordered = list.OrderBy(pdotName).ThenBy(pdotID);
//equals: list.OrderBy(p=>p.Name).ThenBy(p=>p.Id)
Charlie
  • 1,292
  • 8
  • 7