5

I would like my Web API to be able to sort its output by a string parameter such as this one:

http://myapi.com/api/people?skip=0&take=50&orderBy=lastName&descending=true.

Because I also have pagination support (with skipand take) in my API, I would like the orderBy and descending parameter to be applied to the SQL query directly, so that the correct result comes from the database.

When doing this however, the code can become very hard to manage when trying to match the parameters for orderBy with the actual properties of the classes I wish to sort by just using string comparisons.

I have found a solution which is supposed to work with LINQ to Entities and thus also with the new EF7, however when I try to compile this code using the new Core CLR, I get the following message:

Error CS1503 Argument 2: cannot convert from 'System.Linq.Expressions.Expression>' to 'string'

The code from the solution that fails is the OrderBy<T>method:

public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName)
{
    return source.OrderBy(ToLambda<T>(propertyName));
}

It seems like the new Core CLR does not support this attempt. Is there another way to get the solution to work with the new CLR? If no, what other alternatives do I have to enable sorting using EF7 without resulting in countless if or switch statements to compare the input strings to the property names?

Community
  • 1
  • 1
maxmantz
  • 702
  • 1
  • 10
  • 25
  • 2
    Why don't you use OData on your Web API? http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api – juliano.net Jan 20 '16 at 12:23
  • Which line of code gives you this error? Include the snippet that's failing here. – ChrisF Jan 20 '16 at 12:23
  • I've looked at OData before. Does it work correctly with the new ASP.NET 5 and EF7? Does it map the sorting parameters to the SQL query correctly? – maxmantz Jan 20 '16 at 12:25
  • @ChrisF the error is in the `OrderBy`method at `return source.OrderBy(ToLambda(propertyName));` – maxmantz Jan 20 '16 at 12:28
  • 1
    oData works correctly with asp.net 5 and EF7. – Anil Jan 20 '16 at 12:30
  • Can you include `ToLambda(propertyName)` method? – Alexander Derck Jan 20 '16 at 12:37
  • 1
    I guess that you try to call .OrderBy(ToLambda(propertyName)) instead of .OrderBy(propertyName) and then since you are using overloads things all get messed up. on your actual list try to do myList.OrderBy(propertyName); it might give a compiler error which would mean that your list is not typeof(IQueryable) then you know you have to do myList.AsQueryable().OrderBy(propertyName); it has nothing to do with the CLR just you mixing up the overloads you took from that other post. – woutervs Jan 20 '16 at 12:43
  • @JulianoNunesSilvaOliveira I'm intrigued by OData and spent the last couple of hours researching it. However I cannot find a library that supports the latest build of ASP.NET 5. The latest [github](https://github.com/OData/WebApi) build is 8 months old and seems very much dead. – maxmantz Jan 20 '16 at 19:56
  • Are you using ASP.NET 5 in production already? It still is Release Candidate – juliano.net Jan 20 '16 at 20:46
  • No, but I'm working on a project that will incorporate the new ASP.NET 5 framework. Still ways to go before production. – maxmantz Jan 20 '16 at 23:56

1 Answers1

9

The solution from your link uses an "Expression.Convert" which most of the time doesn't work with LINQ to Entities.

Here is a working extension method:

public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string propertyName)
{
    // LAMBDA: x => x.[PropertyName]
    var parameter = Expression.Parameter(typeof(TSource), "x");
    Expression property = Expression.Property(parameter, propertyName);
    var lambda = Expression.Lambda(property, parameter);

    // REFLECTION: source.OrderBy(x => x.Property)
    var orderByMethod = typeof(Queryable).GetMethods().First(x => x.Name == "OrderBy" && x.GetParameters().Length == 2);
    var orderByGeneric = orderByMethod.MakeGenericMethod(typeof(TSource), property.Type);
    var result = orderByGeneric.Invoke(null, new object[] { source, lambda });

    return (IOrderedQueryable<TSource>)result;
}

Disclaimer: I'm the owner of the project EF+ on GitHub.

You can find other methods to order by property name in my repository: GitHub

  • OrderByDescending
  • ThenBy
  • ThenByDescending
  • AddOrAppendOrderBy
  • AddOrAppendOrderByDescending

EDIT: Answer sub-question

Is it possibly to sort by navigation properties using something like this, e.g. a property name "NavigationProperty.PropertyName"

Yes, you can either split the string and loop to create the expression with the property path or use a real expression evaluator.

Disclaimer: I'm the owner of the project Eval-Expressions.NET

This library allows you to execute all LINQ method dynamically.

See: LINQ Dynamic

var result = list.OrderByDynamic(x => "NavigationProperty.PropertyName");
Jonathan Magnan
  • 10,874
  • 2
  • 38
  • 60
  • Is it possibly to sort by navigation properties using something like this, e.g. a property name "NavigationProperty.PropertyName" – Mark Redman Apr 08 '17 at 05:46