2015-03-03 32 views
12

我簡化LINQ Join加上兩個表的Where看起來是這樣的:等價查詢和方法(拉姆達)語法加入與Where子句

var join = context.Foo 
    .Join(context.Bar, 
    foo => new { foo.Year, foo.Month }, 
    bar => new { bar.Year, bar.Month }, 
    (foo, bar) => new { foo.Name, bar.Owner, foo.Year }) 
    .Where(anon => anon.Year == 2015).ToList(); 

或者我可以用下面的語法,我希望相當於:

var joinQuery = from foo in context.Foo 
       join bar in context.Bar 
       on new { foo.Year, foo.Month } equals new { bar.Year, bar.Month } 
       where foo.Year == 2015 
       select new { foo.Name, bar.Owner }; 
var join = joinQuery.ToList(); 

發生在我身上的一個區別是,我想知道的是命令的順序。在lambda語法連接中,我將foo.Year屬性添加到匿名返回類型中,以便我可以過濾,而在另一個查詢中,我仍然可以在where子句中使用foo(和bar,如果需要)。如果我不需要或不需要,我不需要在此處添加字段foo.Year到我的返回類型。

不幸的是,我沒有ReSharper或類似的東西,可以將低級語句轉換爲lambda表達式,以便我可以進行比較。

我其實可以做(和利用上的語句在結構上與下一個更相似)是添加Where(..)ToList()之間以下行中的第一個:

.Select(anon => new { /* the properties I want */ }) 

,但不與第二條語句相比,這只是增加了「多一個」匿名類型創建,或者我誤解了嗎?

總之:什麼是第二條語句的等效Join語法?或者是第一個加上Select加上真的等價,也就是joinQuery在內部是否產生相同的代碼?

回答

6

在一般情況下,你不能總是查詢理解語法和lambda語法之間完全相同同樣的方式,編譯器轉換。這是由於使用transparent identifiers。但是你可以解決這個問題,併產生語義等價的 lambda語句。這就是ReSharper所做的。

不管怎樣,在你的情況,你可以添加:

.Select(anon => new { /* the properties I want */ }) 

這將實例每行一個匿名類型,但它不會是「一多」,所以不用擔心即:該表達式轉換爲SQL,因此join中的new { foo.Year, foo.Month }語句沒有真正實例化這些對象,它們只是轉換爲SQL。只有最後一個選擇將用於SQL SELECT列表,並且一旦檢索到行,則用於對象水合。

1

總之:你的問題的一部分:答案是肯定的。
這裏是resharpers導致

var joinQuery = context.Foo.Join(context.Bar, foo => new 
{ 
    foo.Year, 
    foo.Month 
}, bar => new 
{ 
    bar.Year, 
    bar.Month 
}, (foo, bar) => new 
{ 
    foo, 
    bar 
}).Where(@t => @t.foo.Year == 2015).Select(@t => new 
{ 
    @t.foo.Name, 
    @t.bar.Owner 
}); 
5

但是這與第二條語句相比只是增加了「多一個」匿名類型創建,還是我誤解了?

因爲dotctor的答案顯示:這是編譯器在這種情況下使用comprehension語法時所要做的。通過在單一匿名類型中包含年份,您可以稍微減少開銷。

但是:

  • 匿名類型都非常輕巧:使用泛型,並在實例引用類型的泛型共享的實現意味着有少的代碼(採取反編譯看看組裝)。
  • 當創建大量實例時,對於大多數情況下,它們將在第0代時被清除,因爲它們幾乎立即被釋放。
  • C#編譯器和JIT的優化器在這裏有很多工作。這是很可能的捷徑(但你需要從正在運行的進程讀取x86/x64程序集才能看到)。

記得前兩個rules of optimisation:除非你能證明 - 從實際的測試數據剖析數據 - 你有性能問題,重點明確的代碼,易於維護。

3

另一種辦法是join方法之前移動where方法,然後離開一年了匿名類:

var join = context.Foo 
    .Where(foo => foo.Year == 2015) 
    .Join(context.Bar, 
     foo => new { foo.Year, foo.Month }, 
     bar => new { bar.Year, bar.Month }, 
     (foo, bar) => new { foo.Name, bar.Owner }) 
    .ToList(); 

但總的來說其他的答案是正確的,有沒有太大的區別編譯器可以爲你處理細節。