动态Lambda表达式调用

时间:2015-05-16 03:20:59

标签: c# linq generics expression

运行此代码时,我收到此异常。

  

System.Int64类型的ParameterExpression不能用于System.Object类型的委托参数

我知道这与代码的Expression.Lambda<func<object,bool>>部分有关。总的来说,我希望将任何类型的ParameterExpression传递给此方法,它将调用表达式。

public static IQueryable<T> OrderData<T>(IQueryable<T> data)
{
    try
    {
        Order order = Order.ASC;
        var result = Enum.TryParse<Order>(_gridSettings.SortOrder, true, out order);
        if (_gridSettings.IsSearch)
        {
            data = ExpressionSort(order, data, typeof(T).GetProperty(_gridSettings.SortColumn));
        }
        else
        {
            data = ExpressionSort(order, data, _defaultColumn);
        }
    }
    catch (Exception ex)
    {
        log.WriteLog(MethodBase.GetCurrentMethod(), LogLevel.FATAL, ex);
    }
    return data;
}

private static IQueryable<T> ExpressionSort<T>(Order order, IQueryable<T> data, PropertyInfo property)
{
    // Compose the expression tree that represents the parameter to the predicate.
    ParameterExpression paramExpression = Expression.Parameter(property.PropertyType, property.Name);
    IQueryable<T> queryableData = data.AsQueryable<T>();
    switch (order)
    {
        case Order.ASC:
            return ExecuteCall(paramExpression, paramExpression, queryableData, "OrderBy");
        case Order.DESC:
            return ExecuteCall(paramExpression, paramExpression, queryableData, "OrderByDescending");
    }
    return data;
}

private static IQueryable<T> ExecuteCall<T>(Expression expression, ParameterExpression paramExpression, IQueryable<T> queryableData, string linqMethod)
{
    MethodCallExpression callExpression = Expression.Call(
                               typeof(Queryable),
                               linqMethod,
                               new Type[] { queryableData.ElementType },
                               queryableData.Expression,
                               Expression.Lambda<Func<object, bool>>(expression, new ParameterExpression[] { paramExpression }));
    // Create an executable query from the expression tree.
    return queryableData.Provider.CreateQuery<T>(callExpression);
}

修改 我确实看到了类似问题的答案

Expression of type 'System.Int32' cannot be used for return type 'System.Object' 我不知道如何将它应用于我的代码

编辑2: 主要问题是这条Expression.Lambda<Func<object, bool>>(conversion, new ParameterExpression[] { paramExpression }));行给了我一个例外。 paramExpression包含一个Int64但它期望一个对象。我不知道如何动态告诉Func我已经拥有的信息或者是否可能。

目标: 我正在尝试做这样的事情data.OrderBy(x=>x.DynamicProperty);

3 个答案:

答案 0 :(得分:3)

这就是你要求的,我想......我已经测试了它,它似乎有效。

// Caching of the reflection
private static readonly MethodInfo orderByMethod = GetOrderByMethod("OrderBy");
private static readonly MethodInfo orderByDescendingMethod = GetOrderByMethod("OrderByDescending");

private static IOrderedQueryable<TSource> ExpressionSort<TSource>(Order order, IQueryable<TSource> source, PropertyInfo property)
{
    // Compose the expression tree that represents the parameter to 
    // the predicate.

    // The expression you would use is source => source.Property,

    // The parameter of the lambda, source
    ParameterExpression sourceExpression = Expression.Parameter(typeof(TSource), "source");

    // Accessing the expression
    MemberExpression propertyExpression = Expression.Property(sourceExpression, property);

    // The full lambda expression. We don't need the 
    // Expression.Lambda<>, but still the keySelector will be an
    // Expression<Func<,>>, because Expression.Lambda does it 
    // authomatically. LambdaExpression is simply a superclass of 
    // all the Expression<Delegate>
    LambdaExpression keySelector = Expression.Lambda(propertyExpression, sourceExpression);

    // The OrderBy method we will be using, that we have cached
    // in some static fields
    MethodInfo method = order == Order.ASC ? orderByMethod : orderByDescendingMethod;

    // Adapted from Queryable.OrderBy (retrieved from the reference
    // source code), simply changed the way the OrderBy method is
    // retrieved to "method"
    return (IOrderedQueryable<TSource>)source.Provider.CreateQuery<TSource>(Expression.Call(null, method.MakeGenericMethod(new Type[]
    {
        typeof(TSource),
        property.PropertyType
    }), new Expression[]
    {
        source.Expression,
        Expression.Quote(keySelector)
    }));
}

private static MethodInfo GetOrderByMethod(string methodName)
{
    // Here I'm taking the long and more correct way to find OrderBy/
    // OrderByDescending: looking for a public static method with the
    // right name, with two generic arguments and that has the 
    // parameters related to those two generic arguments in a certain
    // way (they must be IQueryable<arg0> and Expression<Func<arg0,
    // arg1>>
    MethodInfo orderByMethod = (from x in typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                                where x.Name == methodName
                                let generics = x.GetGenericArguments()
                                where generics.Length == 2
                                let parameters = x.GetParameters()
                                where parameters.Length == 2 &&
                                    parameters[0].ParameterType == typeof(IQueryable<>).MakeGenericType(generics[0]) &&
                                    parameters[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(generics))
                                select x).Single();

    return orderByMethod;
}

请不要使用AsQueryable<>()。它不符合您的想法,在单元测试和非常具体的用例之外完全没用。

答案 1 :(得分:1)

您可以使用我的OrderByString扩展名。 https://www.nuget.org/packages/OrderByString/它需要排序参数的字符串。排序参数字符串可以是以逗号分隔的属性名称列表,例如&#34; Prop1,Prop2和#34;或者它可以包括排序顺序,如&#34; Prop1 DESC,Prop2 ASC&#34;。

using OrderByExtensions;

public static IQueryable<T> OrderData<T>(IQueryable<T> data)
{
    try
    {
        Order order = Order.ASC;
        var result = Enum.TryParse<Order>(_gridSettings.SortOrder, true, out order);

        var sortColumn = _gridSettings.IsSearch ? _gridSettings.SortColumn : _defaultColumn;

        data = data.OrderBy(sortColumn + " " + _gridSettings.SortOrder.ToString());
    }
    catch (Exception ex)
    {
        log.WriteLog(MethodBase.GetCurrentMethod(), LogLevel.FATAL, ex);
    }
    return data;
}

OR

您可以使用以下GetExpressionForProperty方法返回OrderBy,OrderByDescending,ThenBy或ThenByDescending的预期排序表达式。

private static IQueryable<T> ExpressionSort<T>(Order order, IQueryable<T> data, PropertyInfo property)
{
    Expression<Func<T, object>> propertyExpression = GetExpressionForProperty<T>(property);

    return order == Order.DESC ? data.OrderByDescending(propertyExpression) : data.OrderBy(propertyExpression);
}

static Expression<Func<TSource, object>> GetExpressionForProperty<TSource>(PropertyInfo propertyInfo)
{
    var param = Expression.Parameter(typeof(TSource));

    return Expression.Lambda<Func<TSource, object>>(
        Expression.Convert(
            Expression.Property(param, propertyInfo),
            typeof(object)
        )
        , param);
}

答案 2 :(得分:0)

尝试使用Expression.Convert。这是一个类似的问题,可以为您提供更多指导:

Expression of type 'System.Int32' cannot be used for return type 'System.Object'