Не удается найти OrderBy в Queryable с «предоставленными аргументами». - PullRequest
8 голосов
/ 01 августа 2011

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

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

1 Ответ

11 голосов
/ 01 августа 2011

Поскольку вы хотите, чтобы ваше выражение селектора свойств выполняло соответствующие вызовы динамически, вы должны создать для него новое выражение.Вы не можете использовать предоставленный селектор как есть, так как он в настоящее время набран 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);
}

Хорошей альтернативой этому было бы сделать тип свойства также универсальным.Таким образом, с самого начала вы получите строго типизированный селектор.

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