【问题标题】:Dynamic Lambda Expression call动态 Lambda 表达式调用
【发布时间】:2015-05-16 03:20:58
【问题描述】:

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

System.Int64 类型的参数表达式不能用于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: 主要问题是 thisExpression.Lambda&lt;Func&lt;object, bool&gt;&gt;(conversion, new ParameterExpression[] { paramExpression })); 行给了我一个例外。 paramExpression 包含一个 Int64 但它期望一个对象。我不知道如何从我已经拥有的信息中动态地告诉 Func,或者是否有可能。

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

【问题讨论】:

  • 您正在使用 T 并且您的参数是一个结构...您不能将结构转换为对象
  • 你能发布一个你如何使用它的例子吗?没有它就很难评估如何解决它。
  • 我贴出了调用其他两个方法的方法。现在没有比这更高的了,只是因为它还不起作用。 IQueryable&lt;T&gt; data 会是这样的 List&lt;MyClass&gt; data @EBrown
  • MyClass 会是什么样子? _gridSettings 是什么? _defaultColumn 是什么?
  • 为了保持示例简单,MyClass 将只包含prop1,即int_gridSettings 只是一个对象,它会告诉我按 asc 或 desc 排序哪一列。 defaultColumn 是列的PropertyInfo

标签: c# linq generics expression


【解决方案1】:

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

// 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&lt;&gt;()。它并没有按照你的想法做,并且在单元测试和非常具体的用例之外完全没用。

【讨论】:

  • 这正是我想要的。我没有意识到它有多复杂。
  • 另外,如果你能发布一些信息来解释为什么这比另一个更有效。
  • @EBrown 评论了所有内容。
【解决方案2】:

您可以使用我的 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 方法,该方法返回 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);
}

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 我确实在那里看到了那个答案,但我没有如何将那个答案应用到我的代码中。我不知道该放什么到Func&lt;this part,bool&gt;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多