2011-04-29 121 views
1

我動態生成要發送到LINQ to Entities的表達式樹。我提供了一個框架,我允許開發人員將輸出列指定爲lambda表達式。將2個LambdaExpressions合併爲1個LamdaExpression

例如,說他們有一列,他們可以指定的方式來拉動值從數據庫是:

p => p.Aliases.Count() 

和另一列是:

p => p.Names.Where(n => n.StartsWith("hello")) 

這裏的問題是我需要將這兩個組合成一個表達式。

我第一次嘗試是:

getter = field.SelectorExpression.Body; 

我得到錯誤信息The parameter 'p' was not bound in the specified LINQ to Entities query expression因爲因爲LINQ可以不知道什麼P是p.Aliases.Count()使。

我想接下來的事情是:

getter = Expression.Invoke(field.OrderBySelector, new[] {parameter}); 

不過,後來我收到消息The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.,因爲,因爲我不希望SQL Server來知道如何運行lambda表達式這也令。

現在基本上有這樣的表達:

item => new MyClass 
{ 
    SomeValue = item.Name, // this was generated from some other code 
    AnotherValue = item.SomeOtherColumn, // there can be lots of these 
    AliasCount = p.Aliases.Count() // here of course is the problem 
} 

顯然,我想用「P」來代替表達時,我建立這個與項目表達(我卻並知道如何使用) 。

TLDR是否有一種簡單的方法可以將LambdaExpression中使用的參數的所有實例替換爲另一個表達式?

+1

像http://stackoverflow.com/questions/5430996/replacing-the-parameter-name-in-the-body-of-an-expression/5431309#5431309也許?讓我知道,如果這回答它(「訪問者」的方法是第一個嘗試) – 2011-04-29 19:31:08

+0

你願意解釋它好一點嗎?我讀了所有,但不明白:/ p => p.Names.Where(n => n.StartsWith(「hello」)) - 是單個表達式,其中單個表達式的參數中的一個參數是另一個表達式表達... – 2011-04-29 19:34:06

+0

Marc Gravell,你簡直就是我的男人之神。你回答這個問題比我能把它放在我的代碼中並測試它! – tster 2011-04-29 19:40:44

回答

0

一個讓你開始的例子。

class Program 
    { 
     static void Main(string[] args) 
     { 
      Expression<Func<Foo1, int>> expression1 = a => a.Foo2S.Count(); 

      Expression<Func<IEnumerable<Foo1>, IEnumerable<Foo2>>> expression2 = 
       a => a.Select(b => new Foo2 { String1 = "asdf", Foo2Count = 3 }); 

      MemberAssignment foo2countAssignment = GetExpression(expression2); 
      // in this case, it will be a ConstantExpression with a value of 3. 
      var expression = foo2countAssignment.Expression as ConstantExpression; 

      Expression<Func<IEnumerable<Foo1>, IEnumerable<Foo2>>> expression3 = 
       a => a.Select(b => new Foo2 { String1 = "asdf", Foo2Count = b.Foo2S.Count() }); 

      foo2countAssignment = GetExpression(expression3); 
      // in this case, it will be an Expression<Func<Foo1, int>> 
      // exactly the same as expression1, except that it has a different parameter. 
      var expressionResult = foo2countAssignment.Expression as MethodCallExpression; 
      var foo2SPropertyExpression = expressionResult.Arguments[0] as MemberExpression; 
      // This is the "b".Foo2SCount() 
      var theBparameter = foo2SPropertyExpression.Expression as ParameterExpression; 


      // Practical demonstartion. 
      var mce = expression1.Body as MethodCallExpression; 

      var selectStatement = expression2.Body as MethodCallExpression; 
      var selectLambda = selectStatement.Arguments[1] as Expression<Func<Foo1, Foo2>>; 
      var bParameter = selectLambda.Parameters[0]; 

      var me = mce.Arguments[0] as MemberExpression; 
      var newExpression = me.Update(bParameter); 
      // Then you go up the expression tree using Update to create new expression till first level. 
      // Unless you find a way to replace me. 
     } 

     public static MemberAssignment GetExpression(Expression<Func<IEnumerable<Foo1>, IEnumerable<Foo2>>> expression2) 
     { 
      // a."Select" 
      var selectStatement = expression2.Body as MethodCallExpression; 
      // a.Select("b => new Foo2..." 
      var selectLambda = selectStatement.Arguments[1] as Expression<Func<Foo1, Foo2>>; 
      // a.Select(b => "new Foo2" 
      var newFoo2Statement = selectLambda.Body as MemberInitExpression; 
      // a.Select(b => new Foo2 {string1 = "asdf", !!Foo2Count = 3!! }) 
      return newFoo2Statement.Bindings[1] as MemberAssignment; 
     } 
    } 

     public class Foo1 
    { 
     public IEnumerable<Foo2> Foo2S { get; set; } 
    } 

    public class Foo2 
    { 
     public string String1 { get; set; } 
     public int Foo2Count { get; set; } 
    } 

基本上這個程序遍歷表達式樹並且展示每個節點。您可以在每個節點上使用「.GetType()」來獲取確切類型的表達式並相應地處理它們。 (在這個例子中,硬編碼和已知的)。

最後一個例子演示瞭如何將a.Foo2s.Count()中的「a」替換爲「b」,以便它可以替換爲第二個更長的表達式。

然後ofcourse你需要想辦法讓你可以自動檢測和複製的所有表達1 backinto表達2.

不是一件容易的事。

+0

請注意,這個SO問題http://stackoverflow.com/questions/5430996/replacing-the-parameter-name-in-the-body-of-an-表達/ 5431309#5431309直接回答我的問題。 – tster 2011-05-12 13:08:32