我有一个方法可以用来对列表进行排序:
private static IQueryable<T> BuildQuery<T>(IQueryable<T> query,
string methodName,
Expression<Func<T, object>> property)
{
var typeArgs = new[] { query.ElementType, property.Body.Type };
methodCall = Expression.Call(typeof (Queryable),
methodName,
typeArgs,
query.Expression,
property);
return query.Provider.CreateQuery<T>(methodCall);
}
当我使用以下args执行代码时出现异常:
var myPreExistingQuery = new List<SomeType>{ new SomeType() }.AsQueryable();
var query = BuildQuery(myPreExistingQuery, "OrderBy", x => x.SomeProperty);
例外是:
No method 'OrderBy' on type 'System.Linq.Queryable' is compatible with the supplied arguments.
谁能看到我在这里缺少的东西?
编辑:
我尝试了Expression.Call()的另一个重载并得到了相同的异常:
private static IQueryable<T> BuildQuery<T>(IQueryable<T> query, string methodName, Expression<Func<T, object>> propertyExpression)
{
var methodCall = Expression.Call(query.Expression,
methodName,
new[] {query.ElementType, property.Body.Type},
new[] {propertyExpression});
return query.Provider.CreateQuery<T>(methodCall);
}
答案 0 :(得分:11)
由于您希望属性选择器表达式动态地进行适当的调用,因此必须为其创建一个新表达式。您不能按原样使用提供的选择器,因为它当前已键入Expression<Func<T, object>>
而未返回您的特定类型Expression<Func<T, SomeType>>
。您可以通过更改调用的类型参数来接受object
来编译它,但它不会按预期工作,因为它将进行对象引用比较(并且您的LINQ提供程序可能会拒绝它)
要重新创建选择器表达式,您可以执行以下操作:
private static IQueryable<T> BuildQuery<T>(
IQueryable<T> query,
string methodName,
Expression<Func<T, object>> property)
{
var typeArgs = new[] { query.ElementType, property.Body.Type };
var delegateType = typeof(Func<,>).MakeGenericType(typeArgs);
var typedProperty = Expression.Lambda(delegateType, property.Body, property.Parameters);
var methodCall = Expression.Call(
typeof(Queryable),
methodName,
typeArgs,
query.Expression,
typedProperty);
return query.Provider.CreateQuery<T>(methodCall);
}
这样做的一个不错的选择是使属性类型也是通用的。这样,您将从一开始就获得一个适当强类型的选择器。
private static IQueryable<TSource> BuildQuery<TSource, TProperty>(
IQueryable<TSource> query,
string methodName,
Expression<Func<TSource, TProperty>> property)
{
var typeArguments = property.Type.GetGenericArguments();
var methodCall = Expression.Call(
typeof(Queryable),
methodName,
typeArguments,
query.Expression,
property);
return query.Provider.CreateQuery<TSource>(methodCall);
}