动态 Lambda 表达式调用

Dynamic Lambda Expression call

我在 运行 此代码时遇到此异常。

ParameterExpression of type System.Int64 cannot be used for delegate parameter of type 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);

尝试使用 Expression.Convert。这是一个类似的问题,可能会给您更多指导:

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

这就是你要求的,我想...我已经测试过了,它似乎可以工作。

// 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<>()。它不会按照您的想法进行操作,并且在单元测试和非常具体的用例之外完全没有用。

您可以使用我的 OrderByString 扩展。 https://www.nuget.org/packages/OrderByString/ 它接受字符串作为排序参数。排序参数字符串可以是 属性 名称的逗号分隔列表,例如 "Prop1,Prop2" 或者它可以包括 "Prop1 DESC, Prop2 ASC".

中的排序顺序
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;
}

您可以使用以下 GetExpressionForProperty 方法 returns 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);
}