Changing a piece of code into expressions
I'm missing something and I'm not quite sure what, I don't have a lot of experience with LINQ expressions.
I'm trying to change the following piece of code into expressions.
MethodInfo orderByMethod = (from method in typeof(QueryableExtensions).GetMethods()
where method.Name == "OrderBy" && method.GetGenericArguments().Length > 0
select method).First();
MethodInfo orderByGenericMethod = orderByMethod.MakeGenericMethod(new[] { queryable.ElementType });
IQueryable queryable = orderByGenericMethod.Invoke(null, new object[] { queryable, sorting.ColumnName, sorting.Direction }) as IQueryable;
Here is my attempt.
Expression orderByMethodExpression = Expression.Call(typeof(QueryableExtensions), "OrderBy", new[] { queryable.ElementType },
Expression.Constant(queryable),
Expression.Constant(sorting.ColumnName),
Expression.Constant(sorting.Direction));
IQueryable queryable = queryable.Provider.CreateQuery(orderByMethodExpression)
The relevant code.
SortingExpression sorting = SortingExpression.Create(arguments.SortExpression);
IQueryable queryable = enumerable.AsQueryable();
if (sorting != null)
{
MethodInfo orderByMethod = (from method in typeof(QueryableExtensions).GetMethods()
where method.Name == "OrderBy" && method.GetGenericArguments().Length > 0
select method).First();
MethodInfo orderByGenericMethod = orderByMethod.MakeGenericMethod(new[] { queryable.ElementType });
queryable = orderByGenericMethod.Invoke(null, new object[] { queryable, sorting.ColumnName, sorting.Direction }) as IQueryable;
}
object[] items = Enumerable.Cast<object>(queryable).ToArray();
arguments.TotalRowCount = items.Length;
enumerable = items;
The error I get.
No generic method 'OrderBy' on type 'Shilony.Web.UI.WebControls.QueryableExtensions' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.
Just to clarify OrderBy is my own extension method here is the signature of the method.
public static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string propertyName, string direction) where TSource : class
Just to understand what's wrong I changed it to this.
Me开发者_运维技巧thodInfo orderByMethod = (from method in typeof(QueryableExtensions).GetMethods()
where method.Name == "OrderBy" && method.GetGenericArguments().Length > 0
select method).First();
MethodInfo orderByGenericMethod = orderByMethod.MakeGenericMethod(new[] { queryable.ElementType });
Expression orderByMethodExpression = Expression.Call(orderByGenericMethod,
Expression.Constant(queryable, typeof(IQueryable)),
Expression.Constant(sorting.ColumnName, typeof(string)),
Expression.Constant(sorting.Direction, typeof(string)));
queryable = queryable.Provider.CreateQuery(orderByMethodExpression);
And now I get this error.
Expression of type 'System.Linq.IQueryable' cannot be used for parameter of type 'System.Linq.IQueryable1[Shilony.DomainLayer.DomainObjects.Customer]' of method 'System.Linq.IQueryable
1[Shilony.DomainLayer.DomainObjects.Customer] OrderBy[Customer](System.Linq.IQueryable`1[Shilony.DomainLayer.DomainObjects.Customer], System.String, System.String)'
I don't get it, when I invoke it with the same args it works but when I try to turn it all into an expression it fails.
Never mind I figured it out, I should have looked more carefully into the details.
Before I made the call to Expression.Call the expression tree is looking like this.
{System.Linq.IQueryable`1[Shilony.DomainLayer.DomainObjects.Customer] OrderBy[Customer](System.Linq.IQueryable, System.String, System.String)}
After I made the call to Expression.Call the expression turns into the following.
{System.Collections.Generic.List`1[Shilony.DomainLayer.DomainObjects.Customer].OrderBy("LastName", null)}
The problem and my mistake is that I tried to pass CreateQuery the expression that was resulted from the call.
The returned type is a list and because OrderBy is an extension method of IQueryable the method exists there, so it tries to call it again, the problem it fails is because now OrderBy should be called with different amount of arguments System.Linq.IQueryable should be excluded and because of that it throws the following exception.
No generic method 'OrderBy' on type 'Shilony.Web.UI.WebControls.QueryableExtensions' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.
Here is the solution.
Expression orderByExtensionMethodExpression = Expression.Call(typeof(QueryableExtensions), "OrderBy", new[] { queryable.ElementType },
Expression.Constant(queryable),
Expression.Constant(sorting.ColumnName),
Expression.Constant(sorting.Direction, typeof(string)));
queryable = Expression.Lambda(orderByExtensionMethodExpression).Compile().DynamicInvoke() as IQueryable;
You don't need to manually create an expression tree. You can get the compiler to do it for you in a much clearer way. This will make it easier to read and maintain.
Expression<Func<string, string, IOrderedQueryable<T>>> GetOrderByExpression<T>(IQueryable<T> query)
{
//the compiler will compile the anonymous method into an expression tree
Expression<Func<string, string, IOrderedQueryable<T>>> orderByMethodExpression = (string column, string direction) => query.OrderBy(column, direction);
//return the expression
return orderByMethodExpression;
}
精彩评论