Не удается найти OrderBy в Queryable с указанными аргументами.

У меня есть метод, который я хочу использовать для сортировки списка:

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

person Byron Sommardahl    schedule 31.07.2011    source источник
comment
не могли бы вы показать мне конструкцию myPreExistingQuery, пожалуйста?   -  person Femaref    schedule 01.08.2011
comment
Я добавил его над вызовом метода.   -  person Byron Sommardahl    schedule 01.08.2011


Ответы (1)


Поскольку вы хотите, чтобы ваше выражение селектора свойства выполняло соответствующие вызовы динамически, вы должны создать для него новое выражение. Вы не можете использовать предоставленный селектор как есть, так как в настоящее время он имеет тип 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);
}
person Jeff Mercado    schedule 31.07.2011
comment
Это было именно ЭТО. То, что вы сказали, имеет смысл. Я вставил ваш код, и мои тесты начали работать. Спасибо за отличный ответ. - person Byron Sommardahl; 01.08.2011
comment
Я добавил еще одну альтернативу воссозданию выражения. Строго не обязательно делать только исходный код универсальным, вы можете сделать оба типа универсальными. - person Jeff Mercado; 01.08.2011
comment
Ваш вариант будет полезен для других и определенно сделает функцию более гибкой. Но, к сожалению, я не могу сделать тип свойства универсальным, так как я не знаю тип до времени выполнения. - person Byron Sommardahl; 01.08.2011
comment
Ах хорошо. Итак, я думаю, это означает, что вы динамически генерируете селектор свойств. Это все еще хорошее дополнение, если вы всегда знаете выражение во время компиляции. - person Jeff Mercado; 01.08.2011