使用"提供的参数"在Queryable上找不到OrderBy.

Byr*_*ahl 8 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);
    }
Run Code Online (Sandbox Code Playgroud)

当我使用以下args执行代码时出现异常:

var myPreExistingQuery = new List<SomeType>{ new SomeType() }.AsQueryable();
var query = BuildQuery(myPreExistingQuery, "OrderBy", x => x.SomeProperty);
Run Code Online (Sandbox Code Playgroud)

例外是:

No method 'OrderBy' on type 'System.Linq.Queryable' is compatible with the supplied arguments.
Run Code Online (Sandbox Code Playgroud)

谁能看到我在这里失踪的东西?

编辑:

我尝试了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);
    }
Run Code Online (Sandbox Code Playgroud)

Jef*_*ado 11

由于您希望属性选择器表达式动态地进行适当的调用,因此必须为其创建新表达式.您不能按原样使用提供的选择器,因为它当前是键入的,Expression<Func<T, object>>而不是返回您的特定类型Expression<Func<T, SomeType>>.您可以通过将调用的类型参数更改为accept来使其编译,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);
}
Run Code Online (Sandbox Code Playgroud)

这样做的一个很好的替代方法是使属性类型也是通用的.这样,您将从一开始就获得一个适当强类型的选择器.

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);
}
Run Code Online (Sandbox Code Playgroud)