2010-10-19 225 views
1

我試圖創建一個表達式樹,它類似於像進行子查詢:算術運算

SELECT (SELECT Sum(Foo) FROM Bar1) - (SELECT Sum(Foo) FROM Bar2)) 

我想重用2個表達式樹過於複雜重複。

我現在所擁有的是2(簡體)表達式樹:

Expression<Func<Bar, int>> SumBar1 = 
    (bar) => (from b in bar.Something 
       where b.Type = 1 
       select b).Sum(); 

Expression<Func<Bar, int>> SumBar2 = 
    (bar) => (from b in bar.Something 
       where b.Type = 2 
       select b).Sum(); 

我已經嘗試過使用Expression.Subtract

Expression foo = Expression.Subtract(SumBar1, SumBar2); 

這失敗,出現錯誤:

The binary operator Subtract is not defined for the types 'System.Func 2[Bar,System.Int32]' and 'System.Func 2[Bar,System.Int32]'.

我也嘗試使用Expression.Invoke調用樹木:

(Bar)),

Expression.Subtract( Expression.Invoke(SumBar1,Expression.Parameter(typeof(Bar)), Expression.Invoke(SumBar2,Expression.Constant(typeof(Bar))));

但後來我得到:

The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.

有什麼辦法來組合這兩個表達式樹成一個新的樹,減去他們,並沿參數傳遞?

回答

2

這都當爲EF構建Linq查詢動態建立很多時,你幾乎到了那裏。我已經編寫了代碼來手動完成此操作,但使用LinqKit要容易得多。

一旦您使用LinqKit,只需編寫一個lambda表達式來調用兩個子表達式並減去結果。然後在結果表達式上調用「Expand」並保存結果。新表達式不會調用調用,因爲傳遞給內部表達式的參數已被替換到它們的正文中,並且方法調用被刪除。

Expression<Func<Bar, int>> SumBar1 = 
    (bar) => (from b in bar.Something 
       where b.Type = 1 
       select b).Sum(); 

Expression<Func<Bar, int>> SumBar2 = 
    (bar) => (from b in bar.Something 
       where b.Type = 2 
       select b).Sum(); 

Expression<Func<Bar, int>> Combined = (bar) => SumBar1.Invoke(bar) - SumBar2.Invoke(bar); 
Expression<Func<Bar, int>> Result = Combined.Expand(); 
+0

非常感謝! – 2010-10-22 15:27:38

0

現在我不知道EF,但LINQ,這聽起來有點奇怪。你爲什麼想要從另一個減去一個委託?

更合適的會是這樣的:

Expression<Func<Bar, int>> sumBar1 = 
    (bar) => (from b in bar.Something 
       where b.Type = 1 
       select b).Sum(); 

Expression<Func<Bar, int>> sumBar2 = 
    (bar) => (from b in bar.Something 
       where b.Type = 2 
       select b).Sum(); 

Expression<Func<Bar, int>> totalSum = 
    bar => 
       sumBar1(bar) - sumBar2(bar); 

totalSum(DB.GetBar()); 

我要預約,雖然,我還沒有真正進行了測試,這可能是完全錯誤的.. :)

+1

原因是,如果要計算大量Bar實例列表的總和,最終會將該數據庫命中N次。爲了解決這個問題,你需要構建一個表達式樹,這樣就可以生成一個大的SQL查詢。你的代碼創建2個'Func '實例',而不是'Expression >',換句話說,當執行totalSum時,它會觸發DB兩次。 – 2010-10-19 22:00:55

+0

嗯,你的權利。現在將該部分恢復爲原始代碼:) – Onkelborg 2010-10-19 22:05:36

+1

現在,我在'sumBar1(bar)'和'sumBar2'上得到編譯錯誤:「預計會有方法,委託或事件」。這是因爲你不能以這種方式調用「表達式」。您可以調用'.Compile()。調用(bar)'或'Expression.Invoke(sumBar1,Expression.Constant(bar))''。前者導致DB往返,後者導致我在我的問題 – 2010-10-19 22:41:00