我的代码是此处示例的略微修订:
我正在编写一种扩展方法,该方法允许执行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)
}
任何帮助将不胜感激。
答案 0 :(得分:5)
此代码有很多低效率的地方。使用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
调用和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);
避免出现此类错误。