2014-10-17 202 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);