Создайте LINQ к объектам выражение OrderBy на лету

Я пытаюсь добавить orderby выражение на лету. Но когда запрос ниже выполняется, я получаю следующее исключение:

Система. NotSupportedException: Не удалось создать постоянную стоимость типа 'Тип закрытия'. Только типы примитивов ('такие как Int32, Строка и Гуид') поддерживаются в этом контексте.

Странная вещь, я - запрос точно те типы примитивов только.

string sortBy = HttpContext.Current.Request.QueryString["sidx"];
ParameterExpression prm = Expression.Parameter(typeof(buskerPosting), "posting");
Expression orderByProperty = Expression.Property(prm, sortBy);

// get the paged records
IQueryable<PostingListItemDto> query =
   (from posting in be.buskerPosting
    where posting.buskerAccount.cmsMember.nodeId == m.Id
    orderby orderByProperty
    //orderby posting.Created 
    select new PostingListItemDto { Set = posting }).Skip<PostingListItemDto>((page -   1) * pageSize).Take<PostingListItemDto>(pageSize);

Надежда кто-то может пролить некоторый свет на это!

17
задан Michael Petrotta 15 May 2010 в 20:11
поделиться

1 ответ

Вы не можете использовать подобные выражения запроса из-за способа их перевода. Однако вы можете сделать это явно с помощью методов расширения:

string sortBy = HttpContext.Current.Request.QueryString["sidx"];
ParameterExpression prm = Expression.Parameter(typeof(buskerPosting), "posting");
Expression orderByProperty = Expression.Property(prm, sortBy);

// get the paged records
IQueryable<PostingListItemDto> query = be.buskerPosting
    .Where(posting => posting.buskerAccount.cmsMember.nodeId == m.Id)
    .OrderBy(orderByExpression)
    .Select(posting => new PostingListItemDto { Set = posting })
    .Skip<PostingListItemDto>((page -   1) * pageSize)
    .Take<PostingListItemDto>(pageSize);

Сложная задача - получить правильный тип дерева выражений - это будет при редактировании :)

РЕДАКТИРОВАТЬ: Редактирование будет несколько задерживается по разным причинам. В основном вам может потребоваться вызвать общий метод с использованием отражения, поскольку Queryable.OrderBy требует общего Expression > , и хотя похоже, что вы знаете ] source во время компиляции, вы можете не знать тип ключа. Если вы действительно знаете, что он всегда будет упорядочивать (скажем) по int, вы можете использовать:

Expression orderByProperty = Expression.Property(prm, sortBy);
var orderByExpression = Expression.Lambda<Func<buskerPosting, int>>
    (orderByProperty, new[] { prm });

РЕДАКТИРОВАТЬ: Хорошо, похоже, у меня все-таки было время.Вот краткий пример вызова OrderBy с использованием отражения:

using System;
using System.Reflection;
using System.Linq;
using System.Linq.Expressions;

public class Test
{
    static void Main()
    {
        string[] names = { "Jon", "Holly", "Tom", "Robin", "Will" };
        var query = names.AsQueryable();
        query = CallOrderBy(query, "Length");
        foreach (var name in query)
        {
            Console.WriteLine(name);
        }
    }

    private static readonly MethodInfo OrderByMethod =
        typeof(Queryable).GetMethods()
            .Where(method => method.Name == "OrderBy")
            .Where(method => method.GetParameters().Length == 2)
            .Single();

    public static IQueryable<TSource> CallOrderBy<TSource>
        (IQueryable<TSource> source, string propertyName)
    {
        ParameterExpression parameter = Expression.Parameter(typeof(TSource), "posting");
        Expression orderByProperty = Expression.Property(parameter, propertyName);

        LambdaExpression lambda = Expression.Lambda(orderByProperty, new[] { parameter });
        Console.WriteLine(lambda);
        MethodInfo genericMethod = OrderByMethod.MakeGenericMethod
            (new[] { typeof(TSource), orderByProperty.Type });
        object ret = genericMethod.Invoke(null, new object[] {source, lambda});
        return (IQueryable<TSource>) ret;
    }
}

Вы можете легко преобразовать CallOrderBy в метод расширения (например, OrderByProperty ) следующим образом:

public static class ReflectionQueryable
{
    private static readonly MethodInfo OrderByMethod =
        typeof(Queryable).GetMethods()
            .Where(method => method.Name == "OrderBy")
            .Where(method => method.GetParameters().Length == 2)
            .Single();

    public static IQueryable<TSource> OrderByProperty<TSource>
        (this IQueryable<TSource> source, string propertyName)
    {
        ParameterExpression parameter = Expression.Parameter(typeof(TSource), "posting");
        Expression orderByProperty = Expression.Property(parameter, propertyName);

        LambdaExpression lambda = Expression.Lambda(orderByProperty, new[] { parameter });
        Console.WriteLine(lambda);
        MethodInfo genericMethod = OrderByMethod.MakeGenericMethod
            (new[] { typeof(TSource), orderByProperty.Type });
        object ret = genericMethod.Invoke(null, new object[] {source, lambda});
        return (IQueryable<TSource>) ret;
    }    
}

Ваш исходный код будет выглядеть следующим образом:

string sortBy = HttpContext.Current.Request.QueryString["sidx"];
// get the paged records
IQueryable<PostingListItemDto> query = be.buskerPosting
    .Where(posting => posting.buskerAccount.cmsMember.nodeId == m.Id)
    .OrderByProperty(sortBy)
    .Select(posting => new PostingListItemDto { Set = posting })
    .Skip<PostingListItemDto>((page -   1) * pageSize)
    .Take<PostingListItemDto>(pageSize);

(Извинения за форматирование с использованием горизонтальных полос прокрутки ... Я переформатирую позже, если кому-то будет интересно. Или вы можете сделать это за меня, если у вас достаточно репутации;)

{{1} }
46
ответ дан 30 November 2019 в 10:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: