Ошибка при создании динамического запроса с использованием ExpressionTree - PullRequest
0 голосов
/ 29 ноября 2018

Мой код представляет собой небольшую ревизию из примера здесь:

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/how-to-use-expression-trees-to-build-dynamic-queries

Я пишу метод расширения, который позволяет выполнять unionon любое свойство в списке источника / назначенияи имеет следующую сигнатуру

public static IEnumerable<TSource> UnionOn<TSource, TProperty>(
   this IEnumerable<TSource> first, 
   Expression<Func<TSource, TProperty>> expression, 
   IEnumerable<TSource> second)
{

    var finalList = new List<TSource>();
    finalList.AddRange(first);

    var queryableData = finalList.AsQueryable();
    foreach (var item in second.ToList())
    {
        var propertyValue = expression.Compile().Invoke(item);

        // x=>x.ExtendedPropertyId == 'guid_value'
        var sourceObjectParam = Expression.Parameter(typeof(TSource), "x");
        var propertyName = ((MemberExpression)expression.Body).Member.Name;

        var left = Expression.Property(sourceObjectParam, propertyName);
        var right = Expression.Constant(propertyValue);

        var predicateBody = Expression.Equal(left, right);

        MethodCallExpression whereCallExpression = Expression.Call(
            typeof(Enumerable),
            "Where",
            new Type[] { queryableData.ElementType },
            queryableData.Expression,
            Expression.Lambda<Func<TSource, Boolean>>(predicateBody, new ParameterExpression[] { sourceObjectParam }));

        // **** this line causes runtime error *****
        IQueryable<TSource> resultsQuery = queryableData.Provider.CreateQuery<TSource>(whereCallExpression);

        if (resultsQuery.ToList().Any())
            finalList.Add(item);
    }

    return finalList;
}

При генерации метода resultsQuery читается исключение:

System.ArgumentException: 'Argument expression is not valid

Однако, когда я смотрю на отладочное представление сгенерированного выражения в whereCallExpression, оно выглядитхорошо для меня:

.Call System.Linq.Enumerable.Where(
.Constant<System.Linq.EnumerableQuery`1[A]>(System.Collections.Generic.List`1[A]),
.Lambda #Lambda1<System.Func`2[A,System.Boolean]>)

.Lambda #Lambda1<System.Func`2[A,System.Boolean]>(A $x) {
$x.ExtendedPropertyId == .Constant<System.Guid>(fadd6b4e-8d97-404c-bcf1- 
 c5ebd02230a6)

}

Любая помощь будет высоко ценится.

1 Ответ

0 голосов
/ 29 ноября 2018

В этом коде много недостатков.И никакого реального преимущества использования выражений при работе с параметрами IEnumerable s - Func<..>, аналогичными стандартным методам LINQ Enumerable, будет недостаточно.

Но для ответа на ваш конкретный вопрос.Исключением является то, что вы вызываете CreateQuery с выражением, возвращающим IEnumerable<TSource> (из-за вызова метода Enumerable.Where), в то время как оно ожидает (требует) IQueryable (или IQueryable<TSource>) выражение типа.

исправить это просто - замените typeof(Enumerable) на typeof(Queryable) в вашем Expression.Call (остальные параметры не изменились), и проблема исчезнет.

Также обратите внимание, что когда у вас есть время компиляции, введите TSource иIQueryable<TSource> и Expression<Func<TSource, bool>> переменных, нет необходимости составлять Where call и CreateQuery - вы можете просто использовать метод расширения Queryable.Where напрямую, например, учитывая

var predicate = Expression.Lambda<Func<TSource, bool>>(predicateBody, sourceObjectParam);

MethodCallExpression whereCallExpression = Expression.Call(
    typeof(Queryable),
    "Where",
    new Type[] { queryableData.ElementType },
    queryableData.Expression,
    predicate);

IQueryable<TSource> resultsQuery =  
    queryableData.Provider.CreateQuery<TSource>(whereCallExpression);

можно заменить на

IQueryable<TSource> resultsQuery = queryableData.Where(predicate);

, что позволяет избежать ошибок, подобных этой.

...