3

I cannot get the dynamic OrderBy to work on my generic list;

var list = CacheObjects.CompetencyAssessments
                       .Select(x => new CompetencyAssessmentLineViewModel(x))
                       .ToList();

var sortInfo = string.Format("{0} {1}", request.SortingName, request.SortingOrder);

var displayList = list.AsQueryable()
                      .OrderBy(sortInfo)
                      .Skip(startIndex)
                      .Take(pageLength);

I am using a string for the dynamic functionality of OrderBy. But the code does not compile;

Error 1 The type arguments for method 'System.Linq.Queryable.OrderBy(System.Linq.IQueryable, System.Linq.Expressions.Expression>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

What am I doing wrong?

The signature of the method is:

public JsonResult GridData(JqGridRequest request)

and JqGridRequest is from the NuGet package Lib.Web.Mvc. So:

  • request.SortingName is a string with the name of the field, and
  • request.SortingOrder is the sort order

See: http://tpeczek.com/2011/03/jqgrid-and-aspnet-mvc-strongly-typed.html

tpeczek
  • 23,867
  • 3
  • 74
  • 77
arame3333
  • 9,887
  • 26
  • 122
  • 205
  • 1
    @Seb Why does it matter to you? It's something holding two string values... – Servy Mar 14 '14 at 16:51
  • 2
    What `dynamic OrderBy`? Unless you added some library with an OrderBy(string) extension, OrderBy expects an Expression (as the error says), not a string. Perhaps you forgot to add a reference? – Panagiotis Kanavos Mar 14 '14 at 16:52
  • Because as far as I can imagine, it must be related to the sorting feature, so I need to know how to include this point in an elaborated answer. – Seb Mar 14 '14 at 16:52
  • The signature of the method is public JsonResult GridData(JqGridRequest request), and JqGridRequest is from the NUGET package Lib.Web.Mvc. So request.SortingName is a string with the name of the field and request.SortingOrder is the sort order, see http://tpeczek.blogspot.co.uk/2011/03/jqgrid-and-aspnet-mvc-strongly-typed.html – arame3333 Mar 14 '14 at 16:54
  • @PanagiotisKanavos - I used the AsQueryable extension which I expected would allow OrderBy to accept a string. Clearly it hasn't but I do not know why. – arame3333 Mar 14 '14 at 16:56
  • 1
    @arame3333 No, that's not at all what `AsQueryable` does. – Servy Mar 14 '14 at 16:57

3 Answers3

3

I suspect you've confused IEnumerable and IQueryable with the Dynamic LINQ library mentioned by Scott Guthrie 6 years ago. This is an external library that you have to add to your project. Its latest version was released as a NuGet package 2 years ago.

The library has some limitations, so another System.Linq.Dynamic project appeared in Codeplex last year.

Neither of the libraries is an official, supported LINQ provider. You can use them if they are convenient but you shouldn't frequent updates to them.

Actually, since you seem to be building an ASP.NET MVC application, it may be better to sort the results in the view or Javascript than try to do so on the server side. Most grids allow sorting by a column.

If you want to sort the results for paging purposes, a better option would be to do so using using your ORM's language, eg Entity SQL for Entity Framework or HQL for NHibernate.

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Actually, there no 'Enumerable.OrderBy()' overload that takes a string as an argument. – Seb Mar 14 '14 at 17:07
  • @Seb There is nothing in this answer stating that there is. – Servy Mar 14 '14 at 17:09
  • @Servy : you're in a bad day, right ? I'm sorry for my poor english, I just meant too add information, not commenting against the answer. Do you want me to close my SO account ? – Seb Mar 14 '14 at 17:11
  • @Seb The use of the word "actually" implies that you are contradicting this answer, not adding unrelated additional information. You also need to stop taking comments personally. – Servy Mar 14 '14 at 17:13
  • @Servy : I can't find a way to open a chat session on the site, so I deeply apologize to go on here for any other reader. The way you talk makes me feel you take the answers and comments personally, but I can be wrong, I often am. I think you might try to speak matter-of-factly (?) but it's a bit harsh, indeed. For being constructive and perfect, where would you have written the content of my comment in order to show the OP that what he is trying to do is not natively supported by Linq ? That would be constructive. – Seb Mar 14 '14 at 17:21
2

The problem was that the Dynamic library was referenced twice in my project. At least I know what to look out for if something like this happens again.

arame3333
  • 9,887
  • 26
  • 122
  • 205
1

As the error message is telling you, you're passing a string to OrderBy wen it's expecting an Expression<Func<CompetencyAssessmentLineViewModel, TKey>>. You need to provide such an expression.

Creating one is hard, because you don't even know the type of the field you are sorting on at compile time. This means that once you generate the proper expression for selecting the property, you have to use reflection to call OrderBy simply because the generic arguments cannot be supplied at compile time.

private static Tuple<Expression, Type> GetSelector<T>(string propertyName)
{
    var parameter = Expression.Parameter(typeof(T));
    Expression body = Expression.Property(parameter, propertyName);

    return Tuple.Create(Expression.Lambda(body, parameter) as Expression
        , body.Type);
}

private static IOrderedQueryable<T> OrderBy<T>(IQueryable<T> query,
    string property, bool ascending)
{
    var selector = GetSelector<T>(property);
    Type[] argumentTypes = new[] { typeof(T), selector.Item2 };
    var methodName = ascending ? "OrderBy" : "OrderByDescending";

    var orderByMethod = typeof(Queryable).GetMethods()
        .First(method => method.Name == methodName
            && method.GetParameters().Count() == 2)
            .MakeGenericMethod(argumentTypes);
    return (IOrderedQueryable<T>)
        orderByMethod.Invoke(null, new object[] { query, selector.Item1 });
}

private static IOrderedQueryable<T> ThenBy<T>(IOrderedQueryable<T> query,
    string property, bool ascending)
{
    var selector = GetSelector<T>(property);
    Type[] argumentTypes = new[] { typeof(T), selector.Item2 };
    var methodName = ascending ? "ThenBy" : "ThenByDescending";

    var orderByMethod = typeof(Queryable).GetMethods()
        .First(method => method.Name == methodName
            && method.GetParameters().Count() == 2)
            .MakeGenericMethod(argumentTypes);
    return (IOrderedQueryable<T>)
        orderByMethod.Invoke(null, new object[] { query, selector.Item1 });
}

public static IOrderedQueryable<T> OrderBy<T>(
    this IQueryable<T> query,
    string property)
{
    return OrderBy<T>(query, property, true);
}
public static IOrderedQueryable<T> OrderByDescending<T>(
    this IQueryable<T> query,
    string property)
{
    return OrderBy<T>(query, property, false);
}
public static IOrderedQueryable<T> ThenBy<T>(
    this IOrderedQueryable<T> query,
    string property)
{
    return ThenBy<T>(query, property, true);
}
public static IOrderedQueryable<T> ThenByDescending<T>(
    this IOrderedQueryable<T> query,
    string property)
{
    return ThenBy<T>(query, property, false);
}

Now that we have all of that in place to order based on a string property, you can do essentially what you wanted to do before:

var displayList = list.OrderBy(request.SortingName)
    .ThenBy(request.SortingOrder)
    .Skip(startIndex)
    .Take(pageLength);
Servy
  • 202,030
  • 26
  • 332
  • 449