Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq: Queryable.OrderBy() using a collection of Expressions

I wonder how I can store orderby expressions in a list. This is what I wanted to write:

List<Expression<Func<Products,Object>>> list = new List<Expression<Func<Products,Object>>>()
{
  p => p.Name,
  p => p.Id
};

Then:

var expr = list[0];
myProducts.OrderBy( expr );

which works for p.Name, but does not work for p.Id (list[1]) as it drops follwing exception

An unhandled exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll Additional information: Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.

What type of list do I have to use?

like image 494
Zoltan Hernyak Avatar asked Jul 01 '14 06:07

Zoltan Hernyak


People also ask

What is OrderBy in LINQ?

Sorts the elements of a sequence in ascending order according to a key. OrderBy<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>, IComparer<TKey>) Sorts the elements of a sequence in ascending order by using a specified comparer.

What algorithm does LINQ OrderBy use?

For LINQ to Objects, it's a stable quicksort that is used.

How does OrderBy work in C#?

In a query expression, the orderby clause causes the returned sequence or subsequence (group) to be sorted in either ascending or descending order. Multiple keys can be specified in order to perform one or more secondary sort operations. The sorting is performed by the default comparer for the type of the element.

How do I use ascending order in LINQ?

In LINQ, the OrderBy operator is used to sort the list/ collection values in ascending order. In LINQ, if we use order by the operator by default, it will sort the list of values in ascending order. We don't need to add any ascending condition in the query statement.


1 Answers

Here's my solution (using Reflection and based on DynamicLinq ideas):

Defining a ConvertableExpression class so we can intercept calls to our custom OrderBy():

public class ConvertableExpression<T>
{
    public ConvertableExpression(Expression<Func<T, object>> expr)
    {
        this.Expression = expr;
    }

    public Expression<Func<T, object>> Expression { get; private set; }
}

Introducing an Extension-Method for easier casting from normal Expression:

public static class ExpressionExtensions
{
    public static ConvertableExpression<T> AsConvertable<T>(this Expression<Func<T, object>> expr)
    {
        return new ConvertableExpression<T>(expr);
    }
}

Extending IQueryable with Reflection-based implementation of OrderBy():

public static class QueryableExtensions
{
    public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, ConvertableExpression<T> expr)
    {
        Expression queryExpr = source.Expression;

        var exprBody = SkipConverts(expr.Expression.Body);
        var lambda = Expression.Lambda(exprBody, expr.Expression.Parameters);
        var quote = Expression.Quote(lambda);

        queryExpr = Expression.Call(typeof(Queryable), "OrderBy", new[] { source.ElementType, exprBody.Type }, queryExpr, quote);

        return (IOrderedQueryable<T>)source.Provider.CreateQuery(queryExpr);
    }

    private static Expression SkipConverts(Expression expression)
    {
        Expression result = expression;

        while (result.NodeType == ExpressionType.Convert || result.NodeType == ExpressionType.ConvertChecked)
            result = ((UnaryExpression)result).Operand;

        return result;
    }
}

Usage:

myProducts.OrderBy(expr.AsConvertable());
like image 147
haim770 Avatar answered Nov 15 '22 00:11

haim770