使用 ExpressionTree 生成动态查询时出错
Error generating Dynamic Query using ExpressionTree
我的代码是对此处示例的轻微修改:
我正在编写一个扩展方法,允许在 source/destination 列表中执行 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)
}
任何帮助将不胜感激。
此代码中存在许多低效之处。在使用 IEnumerable
s 时使用表达式并没有真正的好处 - Func<..>
类似于标准 LINQ Enumerable
方法的参数就足够了。
但要回答你的具体问题。异常是因为您调用 CreateQuery
时表达式返回 IEnumerable<TSource>
(由于 Enumerable.Where
方法调用),而它期望(需要)IQueryable
(或 IQueryable<TSource>
)类型表达式。
修复很简单 - 将 Expression.Call
中的 typeof(Enumerable)
更改为 typeof(Queryable)
(其他参数不变),问题就会消失。
另请注意,当您有编译时类型 TSource
和 IQueryable<TSource>
和 Expression<Func<TSource, bool>>
变量时,无需编写 Where
调用和 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);
从而避免这样的错误。
我的代码是对此处示例的轻微修改:
我正在编写一个扩展方法,允许在 source/destination 列表中执行 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)
}
任何帮助将不胜感激。
此代码中存在许多低效之处。在使用 IEnumerable
s 时使用表达式并没有真正的好处 - Func<..>
类似于标准 LINQ Enumerable
方法的参数就足够了。
但要回答你的具体问题。异常是因为您调用 CreateQuery
时表达式返回 IEnumerable<TSource>
(由于 Enumerable.Where
方法调用),而它期望(需要)IQueryable
(或 IQueryable<TSource>
)类型表达式。
修复很简单 - 将 Expression.Call
中的 typeof(Enumerable)
更改为 typeof(Queryable)
(其他参数不变),问题就会消失。
另请注意,当您有编译时类型 TSource
和 IQueryable<TSource>
和 Expression<Func<TSource, bool>>
变量时,无需编写 Where
调用和 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);
从而避免这样的错误。