无法实现通用的OrderBy解决方案

时间:2017-04-12 12:06:12

标签: c# generics expression-trees

我有一个方法需要IQueryable<T>,我想在其中一般实现OrderBy。理想情况下,通过传入c => c.SomeProperty作为参数,但我无法弄清楚如何让泛型工作,所以我用字符串尝试了它。但是我收到了错误:

Incorrect number of parameters supplied for lambda declaration

这是我尝试的(使用字符串方法)

var sortSelectorParameter = Expression.Parameter(typeof(T), "c");
var sortSelector = Expression.PropertyOrField(sortSelectorParameter, "ClientId"); // ClientId is the property string

collection = collection.OrderByDescending(Expression.Lambda<Func<T, bool>>(sortSelector));

我很困惑,因为OrderBy只接受一个参数 - 任何建议?

3 个答案:

答案 0 :(得分:3)

您需要将参数传递给Expression::Lambda<T>,如错误所示:

var sortSelectorParameter = Expression.Parameter(typeof(T), "c");
var sortSelector = Expression.PropertyOrField(sortSelectorParameter, "ClientId"); // ClientId is the property string

collection = collection.OrderByDescending(Expression.Lambda<Func<T, bool>>(sortSelector, sortSelectorParameter ));

你&#34;身体&#34;对于lambda,引用参数c,由ExpressionParameter实例sortSelectorParameter表示。您需要将此参数实例传递给lambda,以便它知道body引用的参数实际上是您要创建的lambda的in-parameter。

编辑:以上内容可能会回答您的技术问题,但目前尚不清楚您要在此处实现的目标。如果你只是想在编译时通过你知道的东西订购,那么你就不需要这些了。包装OrderByDescending - 方法有什么意义?

IQueryable<TElement> MySpecialOrderBy<TElement, TKey>(IQueryable<TElement> source, Expression<Func<TElement, TKey>> keySelector)
{
    return source.OrderByDescending(keySelector);
}

答案 1 :(得分:1)

这很复杂:

private static readonly MethodInfo OrderByDescending = (from x in typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                                                        where x.Name == "OrderByDescending"
                                                        let args = x.GetGenericArguments()
                                                        where args.Length == 2
                                                        let pars = x.GetParameters()
                                                        where pars.Length == 2 &&
                                                            pars[0].ParameterType == typeof(IQueryable<>).MakeGenericType(args[0]) &&
                                                            pars[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(args))
                                                        select x).Single();

public static IQueryable<T> OrderByStringDescending<T>(this IQueryable<T> query, string parameter)
{
    var par = Expression.Parameter(typeof(T), "obj");
    var selector = Expression.PropertyOrField(par, parameter);
    var lambda = Expression.Lambda(selector, par);
    return (IQueryable<T>)OrderByDescending.MakeGenericMethod(typeof(T), selector.Type).Invoke(null, new object[] { query, lambda });
}

您必须构建Expression.Lambda。问题是在编译时不知道Queryable.OrderByDescending类型而调用IQueryable<>(具有两个参数,Expression<>查询和TKey)时间很复杂。我通过反思来解决它。

答案 2 :(得分:1)

如果您只需要传递一个表达式,那么使用该集合执行其他操作的方法就很简单了:

public void Test<T,TKey>(IQueryable<T> collection, Expression<Func<T,TKey>> orderByExpr)
{
    // your logic here 
    // ...
    collection = collection.OrderByDescending(orderByExpr);
}

你这样称呼它:

this.Test(collection, c => c.ClientId);