Проблемы с выполнением LambdaExpression - PullRequest
3 голосов
/ 07 декабря 2011

H. Я пытаюсь создать запрос linq, который динамически генерирует запрос для пользовательского заказа для динамически отправляемого поля.

Я строительная логика

Expression<Func<string, int>> SpaceStringSortExpression = (a) => a.StartsWith(" ") ? 2 : 1; 

Подпись этого кода (SpaceStringSortExpression.ToString ()) имеет вид "a => IIF (a.StartsWith (\" \ "), 2, 1)"

Чтобы сделать это динамически, я сделал:

ParameterExpression parameter = Expression.Parameter(typeof(TSource), "p1");
            Expression orderByProperty = Expression.Property(parameter, propertyName);
            ConstantExpression c = Expression.Constant(" ", typeof(string));
            MethodInfo mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
            Expression call = Expression.Call(orderByProperty, mi, c);
            Expression<Func<TSource, bool>> lambda = Expression.Lambda<Func<TSource, bool>>(call, parameter);
            ConditionalExpression t = Expression.IfThenElse(call, Expression.Constant(2), Expression.Constant(1));
            //t.tostring() - IIF(p1.Login.StartsWith(" "), 2, 1)
            LambdaExpression callt = Expression.Lambda(t, new[] { parameter });
            //callt.tostring() = p1 => IIF(p1.Login.StartsWith(" "), 2, 1)

Но я не могу заставить его работать, передавая результат в OrderBy

MethodInfo genericMethod;

            genericMethod = OrderByMethod.MakeGenericMethod

            genericMethod = OrderByDescendingMethod.MakeGenericMethod
           (new[] { typeof(TSource), typeof(Int32) });

        object ret = genericMethod.Invoke(null, new object[] { source, callt });
        return (IQueryable<TSource>)ret;

А у меня есть (перевод с локализованного IIS)

Unable to convert "System.Linq.Expressions.Expression`1[System.Action`1[XXXX.User]]" to type "System.Linq.Expressions.Expression`1[System.Func`2[XXXX.User,System.Int32]]".

Весь код:

public static IQueryable<TSource> OrderByPropertyRegardingWhiteSpaces<TSource>
        (this IQueryable<TSource> source, string propertyName, bool ascDirection = true)
        {
  ParameterExpression parameter = Expression.Parameter(typeof(TSource), "p1");
            Expression orderByProperty = Expression.Property(parameter, propertyName);
            ConstantExpression c = Expression.Constant(" ", typeof(string));
            MethodInfo mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
            Expression call = Expression.Call(orderByProperty, mi, c);
            Expression<Func<TSource, bool>> lambda = Expression.Lambda<Func<TSource, bool>>(call, parameter);
            ConditionalExpression t = Expression.IfThenElse(call, Expression.Constant(2), Expression.Constant(1));
            //t.tostring() - IIF(p1.Login.StartsWith(" "), 2, 1)
            LambdaExpression callt = Expression.Lambda(t, new[] { parameter });
            //callt.tostring() = p1 => IIF(p1.Login.StartsWith(" "), 2, 1)
MethodInfo genericMethod;
genericMethod = OrderByMethod.MakeGenericMethod
                (new[] { typeof(TSource), typeof(Int32) });
object ret = genericMethod.Invoke(null, new object[] { source, callt });
            return (IQueryable<TSource>)ret;
   }



        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, bool ascDirection = true)
        {
            ParameterExpression parameter = Expression.Parameter(typeof(TSource), "p1");
            Expression orderByProperty = Expression.Property(parameter, propertyName);

            LambdaExpression call = Expression.Lambda(orderByProperty, new[] { parameter });
MethodInfo genericMethod;
            if (ascDirection)
            {
                genericMethod = OrderByMethod.MakeGenericMethod
                (new[] { typeof(TSource), orderByProperty.Type });
            }
            else
            {
                genericMethod = OrderByDescendingMethod.MakeGenericMethod
               (new[] { typeof(TSource), orderByProperty.Type });
            }
            object ret = genericMethod.Invoke(null, new object[] { source, call });
            return (IQueryable<TSource>)ret;
        }

1 Ответ

6 голосов
/ 07 декабря 2011

Проблема в том, что это:

ConditionalExpression t = Expression.IfThenElse(call, Expression.Constant(2), Expression.Constant(1));

становится выражением типа Action, выражения «then» и «else» выполняются, но не возвращают значение, как вы предполагали. Это не соответствует сигнатуре, требуемой методом OrderBy, поэтому выдает исключение, которое вы видите. Вы хотите это:

ConditionalExpression t = Expression.Condition(call, Expression.Constant(2), Expression.Constant(1));

Тип узла Condition имеет возвращаемое значение. Отличный способ отладки это написать модульный тест, который делает это:

Expression<Func<bool,int>> expr = (a) => a ? 2 : 1

, а затем с помощью отладчика проверить созданное дерево выражений. Надеюсь, это поможет.

...