I have below code snippet and getting an error as mentioned below.
string[] companies = {
"Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light",
"Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works",
"Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders",
"Blue Yonder Airlines", "Trey Research", "The Phone Company",
"Wingtip Toys", "Lucerne Publishing", "Fourth Coffee"
};
var exp = companies.AsQueryable<string>();
// Compose the expression tree that represents the parameter to the predicate.
ParameterExpression pe = Expression.Parameter(typeof(string), "company");
// The IQueryable data to query.
IQueryable<String> queryableData = companies.AsQueryable<string>();
MethodCallExpression orderByCallExpression1 = Expression.Call(
typeof(Queryable),
"OrderBy",
new Type[] { queryableData.ElementType },
Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe })
);
System.InvalidOperationException: 'No generic method 'OrderBy' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic. '
Please guide whats wrong in here?
There are two "problems" with OrderBy
method: it has overloads and it is generic. First you need to select correct overload:
var openOrderBy = typeof(Queryable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.First(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
(note: here I'm using simple check based on parameters count, you can read more here)
Then you need to bind it to concrete types:
var closedOrderBy = openOrderBy.MakeGenericMethod(
typeof(string), // type of item in collection, TSource
typeof(string)); // type returned by lambda, TKey
Now you can use that MethodInfo
in Expression.Call
:
var pe = Expression.Parameter(typeof(string), "company");
var orderByCall = Expression.Call(null, // for static methods
closedOrderBy,
companies.AsQueryable().Expression,
Expression.Lambda<Func<string, string>>(pe, pe));
You can test it with extra lambda:
var result = Expression.Lambda<Func<IQueryable<string>>>(orderByCall)
.Compile().Invoke().ToList();
result.ForEach(Console.WriteLine);
var queryableData = companies.AsQueryable();
var pe = Expression.Parameter(typeof(string), "company");
var orderByCall = Expression.Call(typeof(Queryable),
"OrderBy",
new []{ queryableData.ElementType,
queryableData.ElementType }, // <-- fix #1 select correct overload
queryableData.Expression, // <-- fix #2 pass first argument
Expression.Lambda<Func<string, string>>(pe, pe)
);