4

Possible Duplicate:
Dynamic LINQ OrderBy

I have a list of custom sort options that are passed to the server from a client-side grid control (KendoUI grid if you're wondering). These sort options have the property to sort by as a string. I wrote a switch method that will check the values of the sort object and apply the appropriate LINQ.

    private IQueryable<Report> SortReports(IQueryable<Report> reports, KendoSort sort)
    {
        switch (sort.Field)
        {
            case "name":
                return sort.Dir == "asc" ? reports.OrderBy(x => x.Name) : reports.OrderByDescending(x => x.Name);
            case "description":
                return sort.Dir == "asc" ? reports.OrderBy(x => x.Description) : reports.OrderByDescending(x => x.Description);
            default:
                return sort.Dir == "asc" ? reports.OrderBy(x => x.Id) : reports.OrderByDescending(x => x.Id);
        }
    }

This is working fine but seems so ugly. How might I do this with reflection so that I don't have to write a custom function for every type of entity I want to do this with? It would be nice if I could just have a single function that did this no matter what the entity.

Community
  • 1
  • 1
Chev
  • 54,842
  • 60
  • 203
  • 309

4 Answers4

2

You could use Dynamic LINQ. Here's a blog post about it from Scott Gu.

Darin Dimitrov
  • 960,118
  • 257
  • 3,196
  • 2,876
  • Great solution as well. I'm not sure which one I like more! – Chev Oct 22 '12 at 05:57
  • You just made my life so much easier. Doing this in the repository method: `foreach (var sort in kendoSorts) reports = reports.OrderBy(string.Format("{0} {1}", sort.Field, sort.Dir));` The foreach allows it to handle multiple sortings. – Chev Oct 22 '12 at 06:13
1

Marc Gravell has a great little library called FastMember. You can use it like this:

private IQueryable<Report> SortReports(IQueryable<Report> reports,KendoSort sort)
{
    var accessor = TypeAccessor.Create(typeof(Report));
    return sort.Dir == "asc" ? 
        reports.OrderBy(x => accessor[x,sort.Field]) : 
        reports.OrderByDescending(x => accessor[x,sort.Field]));
}
Eren Ersönmez
  • 36,276
  • 7
  • 63
  • 88
1

Using Reflection, where KendoSort.Property is the PropertyInfo of the Report property that returns the value needed for sorting.

private IQueryable<Report> SortReports(IQueryable<Report> reports, KendoSort sort)
{
    return sort.Dir == "asc" ? reports.OrderBy(x => sort.Property.GetValue(x)) : reports.OrderByDescending(x => sort.Property.GetValue(x));
}

But then, reflection is relatively slow. Other solutions are probably better.

LightStriker
  • 17,096
  • 3
  • 20
  • 26
  • +1 for answering my exact question, even though reflection isn't actually what I wanted in the end :) – Chev Oct 22 '12 at 06:21
1

The following should create the sort function you want dynamically.

ParameterExpression pe = Expression.Parameter(typeof(Report), "x");
LambdaExpression le = Expression.Lambda(
    Expression.PropertyOrField(pe, sort.Field),
    new List<ParameterExpression>() {pe});

var leCompiled = (Func<Report, string>)le.Compile();                

return sort.Dir == "asc" ? reports.OrderBy(leCompiled) : reports.OrderByDescending(leCompiled);

It creates a Func delegate in the form x => x.ReportProperty, where ReportProperty is the value of sort.Field.

WarrenG
  • 3,065
  • 14
  • 10