动态创建LINQ到实体OrderBy表达式

AyK*_*rsi 17 c# linq-to-entities

我正在尝试动态添加orderby表达式.但是当执行下面的查询时,我得到以下异常:

System.NotSupportedException:无法创建类型为"Closure type"的常量值.在此上下文中仅支持原始类型(例如Int32,String和Guid').

奇怪的是,我只查询那些原始类型.

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);
Run Code Online (Sandbox Code Playgroud)

希望有人能对此有所了解!

Jon*_*eet 46

由于它们的翻译方式,你基本上不能使用这样的查询表达式.但是,您可以使用扩展方法显式执行此操作:

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);
Run Code Online (Sandbox Code Playgroud)

棘手的一点是获得正确的表达式树类型 - 这将进入编辑:)

编辑:由于各种原因,编辑将有所延迟.基本上你可能需要使用反射来调用泛型方法,因为Queryable.OrderBy需要泛型Expression<Func<TSource, TKey>>,虽然看起来你在编译时知道类型,但你可能不知道密钥类型.如果您确实知道它总是按(例如)int排序,您可以使用:

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

编辑:好的,看起来我毕竟有时间.这是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以轻松地重构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;
    }    
}
Run Code Online (Sandbox Code Playgroud)

您的原始代码将变为:

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);
Run Code Online (Sandbox Code Playgroud)

(对于涉及水平滚动条的格式化道歉......如果有人关心,我会稍后重新格式化.如果你有足够的代表,你可以为我做这件事;)