问题描述
我正在尝试在 C# 中创建一个表达式树来动态表示以下嵌套的 lambda...
item => selectorList.Any(selector => terms.Any(sTerm => selector.Contains(sTerm))
地点:
- 'item' 是一个泛型类型 T
- 'selectorList' 的类型为 IEnumerable
>> - 'terms' 是 IEnumerable 类型
经过大量工作,我已经到了这里 - 但现在我正在努力把它做好。我对嵌套时如何使用 ParameterExpression 作为解析值缺少一些基本的了解。
public static Expression<Func<T,bool>> CreateWhereAnyContainsLambaExpression<T>(IEnumerable<string> terms,params Expression<Func<T,String>>[] selectorList)
{
// Create ParameterExpressions
ParameterExpression qi = Expression.Parameter(typeof(T),"qi");
ParameterExpression selector = Expression.Parameter(typeof(Expression<Func<T,String>>),"selector");
ParameterExpression sTerm = Expression.Parameter(typeof(string),"sTerm");
// Create ConstantExpressions
ConstantExpression termsConstant = Expression.Constant(terms);
ConstantExpression selectorListConstant = Expression.Constant(selectorList);
// Get MethodInfo
MethodInfo selectorListAny = typeof(Enumerable).getmethods().Where(m => m.Name == "Any").First(m => m.GetParameters().Count() == 2).MakeGenericmethod(typeof(Expression<Func<T,string>>));
MethodInfo termsAny = typeof(Enumerable).getmethods().Where(m => m.Name == "Any").First(m => m.GetParameters().Count() == 2).MakeGenericmethod(typeof(string));
MethodInfo selectorContains = typeof(string).getmethod("Contains",new[] { typeof(string) });
// Build the Expression from inside to out....
// selector.Contains(sTerm)
var expSelectorContains = Expression.Call(selector,selectorContains,sTerm);
// sTerm => ...expSelectorContains...
var sTermlambda = Expression.Lambda<Func<string,bool>>(expSelectorContains,sTerm);
// terms.Any(...sTermlambda...)
var expTermsAny = Expression.Call(termsConstant,termsAny,sTermlambda);
// selector => ...expTermsAny...
var selectorLambda = Expression.Lambda<Func<Expression<Func<T,string>>,bool>>(expTermsAny,selector);
// selectorList.Any(...selectorLambda...)
var expSelectorListAny = Expression.Call(selectorListConstant,selectorListAny,selectorLambda);
// item => ...expSelectorListAny...
var lambda = Expression.Lambda<Func<T,bool>>(expSelectorListAny,qi);
return lambda;
}
失败就在这条线上
// selector.Contains(sTerm)
var expSelectorContains = Expression.Call(selector,sTerm);
我在哪里得到异常
System.ArgumentException : Method 'Boolean Contains(System.String)' declared on type 'System.String' cannot be called with instance of type 'System.Linq.Expressions.Expression`1[System.Func`2[TestObject,System.String]]'
鉴于 sTerm 是树更上层的参数表达式,我如何将其解析表示传递给 Contains 方法?
解决方法
那么让我澄清一下我造成的困惑...
我正在用 selector.Contains(sTerm)
var expSelectorContains = Expression.Call(selector,selectorContains,sTerm);
现在应该针对 selectorContains
的实例调用 string
MethodInfo。但是我的 selector
是 Expression<Func<T,String>>
类型的 ParameterExpression。
我只是将它与传入的 selectorList 变量进行了匹配,因为我应该将它与减少时解析的内容进行匹配......这只是一个字符串。
所以修复是改变
ParameterExpression selector = Expression.Parameter(typeof(Expression<Func<T,String>>),"selector");
到
ParameterExpression selector = Expression.Parameter(typeof(string),"selector");
哦!我的错。真诚感谢您的建议。
,您正在调用 Enumerable.Any
,并尝试传入 Expression<Func<TestObject,string>>
。 Enumerable.Any
采用 Func<TestObject,string>
,而不是 Expression<Func<TestObject,string>>
。
Queryable.Any
但是需要一个 Expression<Func<TestObject,string>>
。
因此,答案取决于您要尝试做什么。你真的想打电话给Enumerable.Any
吗?在这种情况下,您需要将 Expression<Func<TestObject,string>>
编译为实际的 Func<TestObject,string>
,然后您可以传递它。或者,由于您似乎完全在表达式中工作(而不是编译它们),Queryable.Any
会更合适吗?