我需要组合两个lambda表达式,第二个表达式“包含”第一个表达式。 我搜索了很多,但没有找到任何明确的答案......
我想做的是以下内容: 第一个表达式“expression1”作为参数传递给方法,并且仅用于定义第二个lambda必须在哪个字段或属性上运行。
示意图,我正在尝试执行以下操作:
// simple field selector :
Expression<Func<T, string>> expression1 = obj => obj.field;
// trying to use the field selector in 2nd expression :
Expression<Func<T, bool>> e2 = obj => [[Result of expression1]].Equals("myValue");
换句话说,我想得到:
Expression<Func<T, bool>> e2 = obj => obj.field.Equals("myValue");
我需要这样做,因为它并不总是被调用的Equals()
方法,而是许多不同的方法。
我尝试将expression1编译为Func<T, string>
以便在expression2中调用此Func,但是当我在LinQ中使用它时,我得到一个异常,因为LinQ不支持Invoke节点类型。
所以我的问题是:有没有办法将这两个表达式的主体结合起来,请问有多少?
先谢谢!
答案 0 :(得分:3)
嗯,这应该可以解决问题:
void Main()
{
var execute = Create<TestClass>(
first => first.MyStringField, // field selector
second => second.Equals("1234") // method selector
);
Console.WriteLine(execute(new TestClass("1234"))); // true
Console.WriteLine(execute(new TestClass("4321"))); // false
}
class TestClass
{
public string MyStringField;
public TestClass(string val){
MyStringField = val;
}
}
static Func<TSource, bool> Create<TSource>(
Expression<Func<TSource, string>> fieldSelector,
Expression<Func<string, bool>> methodSelector
)
{
// todo: classical validation of fieldSelector, if necessary.
// todo: classical validation of methodSelector, if necessary.
var compiledFieldSelector = fieldSelector.Compile();
var compiledMethodSelector = methodSelector.Compile();
return T => compiledMethodSelector(compiledFieldSelector(T));
}
注意,由于lambda表达式的性质,你需要验证字段选择器和方法选择器,否则它可能会做一些非常奇怪的事情。
或者,下一个方法创建的lambda不会&#34;组成&#34;事实上,这是更好的,因为LinqToEntities在解释查询时不会有问题。
static Func<TSource, bool> Create<TSource>(
Expression<Func<TSource, string>> memberSelector,
Expression<Func<string, bool>> methodSelector
)
{
// todo: classical validation of memberSelector, if necessary.
// todo: classical validation of methodSelector, if necessary.
var memberExpression = (MemberExpression)(memberSelector.Body);
var methodCallExpression = (MethodCallExpression)(methodSelector.Body);
// input TSource => ..
var input = Expression.Parameter(typeof(TSource));
var call = Expression.Call(
Expression.MakeMemberAccess(
input,
memberExpression.Member),
methodCallExpression.Method,
methodCallExpression.Arguments);
return Expression.Lambda<Func<TSource, bool>>(call, input)
.Compile();
}
答案 1 :(得分:1)
Chris Eelmaa的第二个答案是肯定的,只需删除.Compile()调用以避免出现Invoke异常:
static Expression<Func<TSource, bool>> Create<TSource>(
Expression<Func<TSource, string>> memberSelector,
Expression<Func<string, bool>> methodSelector
)
{
// todo: classical validation of memberSelector, if necessary.
// todo: classical validation of methodSelector, if necessary.
var memberExpression = (MemberExpression)(memberSelector.Body);
var methodCallExpression = (MethodCallExpression)(methodSelector.Body);
// input TSource => ..
var input = Expression.Parameter(typeof(TSource));
var call = Expression.Call(
Expression.MakeMemberAccess(
input,
memberExpression.Member),
methodCallExpression.Method,
methodCallExpression.Arguments);
return Expression.Lambda<Func<TSource, bool>>(call, input);
}
在我的情况下,然后像这样使用:
(selector
是一个类似u => u.id
的表达式
request
是IQueryable<T>
,都是作为参数收到的)
Expression<Func<T, bool>> contains = Create<T>(selector, u => u.Contains(searchExpression));
IQueryable<T> result = request.Where(contains);