使用“提供的参数”在Queryable上找不到OrderBy。

时间:2011-07-31 21:59:45

标签: c# linq expression-trees

我有一个方法可以用来对列表进行排序:

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

1 个答案:

答案 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);
}