Отражение с помощью IQueryable

Я играю и пытаюсь создать метод расширения для IQueryable, который сортирует его по произвольному свойству объекта.

public static class IQueryableExtender

{

public static IQueryable<TSource> Sort<TSource>(this IQueryable<TSource> query, string orderProperty, string sortDirection = "asc")      
{

    var elementType = query.ElementType;

    var propertyInfo = elementType.GetProperty(orderProperty);
    if (propertyInfo == null)
    {
         throw new ArgumentException(string.Format("{0} is not a property on {1}", orderProperty, elementType.Name));
    }


    switch (sortDirection.ToLower())
    {
       case "asc":
       case "ascending":
           return query.OrderBy(x => propertyInfo.GetValue(x, null));
           break;
       case "desc":
       case "descending":
           return query.OrderByDescending(x => propertyInfo.GetValue(x, null));
           break;
    }

    return query;
}
}

Я получаю эту ошибку при вызове метода. Я предполагаю, что это связано с тем, что IQueryable еще не выполнил и не получил никаких объектов.

LINQ to Entities не распознает метод System.Object GetValue(System.Object, System.Object[]), и этот метод нельзя преобразовать в выражение хранилища.

Я могу решить эту проблему, выполнив ToList в IQueryable, но затем я получаю данные в методе расширения, а это не то, что мне нужно.

Можно ли это решить?


person Lejdholt    schedule 24.05.2011    source источник


Ответы (1)


LINQ to Entities пытается выполнить ваши запросы в базе данных, когда вы выполняете операцию LINQ с IQueryable ‹>. В вашем case "asc" вы выполняете query.OrderBy, который LINQ to Entities интерпретирует как «преобразовать это в SQL», и он терпит неудачу, поскольку вы используете вызовы отражения, которые он не знает, как преобразовать в SQL.

Вы можете сделать query.AsEnumerable (). OrderBy (...). Одним из следствий этого является то, что при запуске операции OrderBy остальная часть запроса будет выполняться, чтобы данные были предоставлены.

Вместо того, чтобы использовать эти уловки отражения, вы могли бы просто использовать методы OrderBy и OrderByDescending, предназначенные для приема делегата для извлечения значения сортировки. (items.OrderBy(item => item.Property)). Чего вам не хватает, так это возможности указывать возрастание или убывание в рамках одного и того же метода, но я бы просто использовал пару методов, например:

public static IOrderedQueryable<TSource> OrderByAscDesc<TSource, TKey>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, TKey>> keySelector, bool isAsc
) {
    return (isAsc ? source.OrderBy(keySelector) : source.OrderByDescending(keySelector);
}

public static IOrderedQueryable<TSource> OrderByAscDesc<TSource, TKey>(
    this IQueryable<TSource> source,
    Func<TSource, TKey> keySelector, bool ascDesc
) {
    return (isDesc ? source.OrderBy(keySelector) : source.OrderByDescending(keySelector);
}
person Jesper    schedule 24.05.2011
comment
Спасибо за ответ. Это не совсем то, что я ищу. Я не упомянул, что работаю с MVC и получаю имя свойства для сортировки из представления. Поэтому мне нужен был метод, в котором я мог бы просто взять параметр, отразить значение свойства объекта и отсортировать его. Это означало бы, что мне не нужны операторы if, чтобы выбрать, какое свойство использовать с операторами if. - person Lejdholt; 24.05.2011
comment
В этом немного больше смысла. Добавление AsEnumerable () перед использованием OrderBy должно исправить это. - person Jesper; 24.05.2011