使用表达式树生成动态查询时出现错误

4
我的代码是对此处示例的轻微修改:https://learn.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 中生成的表达式的 debugview 时,它在我的眼里看起来很好:

.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个回答

5
这段代码存在许多效率低下的问题。而当处理 IEnumerable 时,使用表达式没有真正的好处 - 使用类似于标准 LINQ Enumerable 方法的 Func<..> 参数就足够了。
但是要回答您具体的问题。异常是因为您在调用返回 IEnumerable<TSource> 的表达式(由于 Enumerable.Where 方法调用)时调用了 CreateQuery,而它期望(要求)IQueryable (或 IQueryable<TSource>)类型的表达式。
修复很简单 - 在您的Expression.Call中将 typeof(Enumerable) 更改为 typeof(Queryable)(其他参数不变),问题就会消失。
另外请注意,当您拥有编译时类型 TSourceIQueryable<TSource> 变量以及 Expression<Func<TSource, bool>> 变量时,没有必要组合 Where 调用和 CreateQuery - 您可以直接使用 Queryable.Where 扩展方法,例如给出的:
var predicate = Expression.Lambda<Func<TSource, bool>>(predicateBody, sourceObjectParam);

the

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);

因此,避免出现这样的错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接