2014-10-17 164 views
2

我需要将第二个表达式“包含”第一个表达式的方式合并为两个lambda表达式。 我搜索了很多,但没有发现任何明确的答案......在另一个lambda表达式中使用lambda表达式

什么,我试图做的是以下几点: 的第一表现“表达式”作为参数的方法通过,并仅用于定义第二个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"); 

我需要做的是这种方式,因为它并不总是该方法将被调用,但有许多不同的方法。

我试图将expression1编译为Func<T, string>以便在expression2中调用此Func,但是因为我在LinQ中使用此函数,所以我得到一个异常,因为LinQ不支持Invoke节点类型。

所以我的问题是:有没有一种方法来结合两个表达式的主体,请问如何?

感谢提前!

回答

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,从某种意义上说,这是更好的,因为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(); 
} 
+0

谢谢Chris Eelmaa。您的解决方案在经典环境中运行良好。不幸的是,当我使用LinQ时,我在运行时得到以下System.NotSupportedException:LINQ to Entities不支持LINQ表达式节点类型“Invoke”。 – 2014-10-17 12:47:50

+0

@ Numer_11:更新后。这是你在找什么? – 2014-10-17 14:24:06

+0

是的!我测试了你的新答案,但仍然有例外。之后,我删除了.Compile()调用,并将Create方法的返回类型更改为匹配。最后,这个小小的修改与LinQ完美结合!非常感谢 ! (发布修改以使其他人明确) – 2014-10-17 15:02:30

1

克里斯Eelmaa的第二个答案是确定的,只是删除.Compile()调用,以避免与调用异常:

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的表达式, requestIQueryable<T>,无论接收到的作为参数)

Expression<Func<T, bool>> contains = Create<T>(selector, u => u.Contains(searchExpression)); 
IQueryable<T> result = request.Where(contains);