无法使用“提供的参数"在Queryable上找到OrderBy. [英] Can't find OrderBy on Queryable with the "supplied arguments".

查看:42
本文介绍了无法使用“提供的参数"在Queryable上找到OrderBy.的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

使用以下参数执行代码时出现异常:

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

解决方案

由于您希望属性选择器表达式动态地进行适当的调用,因此必须为其创建一个新的表达式.您不能按原样使用提供的选择器,因为它当前键入的是Expression<Func<T, object>>并且没有返回您的特定类型Expression<Func<T, SomeType>>.您可能可以通过将调用的类型参数更改为接受object来使其进行编译,但由于它将进行对象引用比较(因此您的LINQ提供程序可能仍会拒绝它),因此它无法按预期工作. /p>

要重新创建选择器表达式,可以执行以下操作:

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

I have a method that I want to use to sort a list:

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

I get an exception when I execute the code using the following args:

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

The exception is:

No method 'OrderBy' on type 'System.Linq.Queryable' is compatible with the supplied arguments.

Can anyone see what I'm missing here?

EDIT:

I tried another overload of Expression.Call() and got the same same exception:

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

解决方案

Since you want your property selector expression to make the appropriate calls dynamically, you must create a new expression for it. You cannot use the provided selector as-is since it is currently typed Expression<Func<T, object>> and not returning your specific type Expression<Func<T, SomeType>>. You might be able to get it to compile by changing the type arguments of the call to accept object but it will not work as expected since it will be doing object reference comparisons (and your LINQ provider will may reject it anyway).

To recreate your selector expression, you could do this:

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

A nice alternative to doing this would be to make the property type generic too. That way, you'll get an appropriately strongly typed selector from the start.

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

这篇关于无法使用“提供的参数"在Queryable上找到OrderBy.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆