表达式树 OrderBy 异常

Expression Trees OrderBy Exception

我有以下代码片段并收到如下所述的错误。

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. '

请指导这里有什么问题?

有两个 "problems" 和 OrderBy 方法:它有重载并且是通用的。首先你需要select纠正过载:

var openOrderBy = typeof(Queryable)
    .GetMethods(BindingFlags.Static | BindingFlags.Public)
    .First(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);

(注意:这里我使用的是简单的基于参数计数的检查,你可以阅读更多here

然后你需要将它绑定到具体类型:

var closedOrderBy = openOrderBy.MakeGenericMethod(
    typeof(string), // type of item in collection, TSource
    typeof(string)); // type returned by lambda, TKey

现在您可以在 Expression.Call 中使用 MethodInfo:

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));

你可以用额外的 lambda 测试它:

var result = Expression.Lambda<Func<IQueryable<string>>>(orderByCall)
    .Compile().Invoke().ToList();

result.ForEach(Console.WriteLine);

Demo


更新: 正如@Ivan Stoev 在评论中所说,答案可以简化

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)
  );