2015-03-03 8 views
12

Moja uprościć LINQ Join Plus Where z dwóch tabel wygląda następująco:Równoważność zapytania oraz sposobu (lambda) składnia Join z 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(); 

Ewentualnie mógłbym stosować następującą składnię, że mam nadzieję być równoważne:

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(); 

Jedna różnica, która występuje dla mnie i że zastanawiam się, to kolejność poleceń. W składni sprzężenia lambda dodam właściwość foo.Year do mojego anonimowego typu zwracania, aby można było filtrować po niej, podczas gdy w drugim zapytaniu nadal mogę użyć foo (i bar, jeśli chcę) w klauzuli where. Nie muszę dodawać pola foo.Year do mojego typu zwrotu tutaj, jeśli nie chcę lub nie potrzebuję.

Niestety nie mam ReSharpera ani niczego podobnego, co mogłoby przetłumaczyć niższe wyrażenie na lambda, żeby móc je porównać.

Co mogę zrobić w rzeczywistości (i sprawiają, że górna oświadczenie bardziej podobne w strukturze do dolnej jeden) jest dodać następujący wiersz między Where(..) i ToList() w pierwszym:

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

Ale nie robi to po prostu dodaje "jeszcze jeden" anonimowy typ w porównaniu do drugiego stwierdzenia, czy też się mylę?

W skrócie: Co to jest odpowiednik składni Join do drugiej instrukcji? Czy pierwszy i dodatkowy Select są naprawdę ekwiwalentne, to znaczy, czy joinQuery wewnętrznie wytwarza ten sam kod?

Odpowiedz

6

W ogólnym przypadku, nie zawsze można konwertować pomiędzy zrozumieniem kwerendy składni i składni lambda w dokładnie ten sam sposób kompilator robi. Wynika to z użycia transparent identifiers. Ale możesz obejść to i wytworzyć semantycznie równoważne oświadczenia lambda. I właśnie to robi ReSharper.

W każdym razie, w danym przypadku, można tylko dodać:

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

To będzie instancję typ anonimowy w każdym wierszu, ale to nie będzie „jeszcze jeden”, więc nie martw się o to: wyrażenie jest konwertowane na SQL, więc instrukcje new { foo.Year, foo.Month } w join tak naprawdę nie tworzą tych obiektów, po prostu konwertują się na SQL. Tylko ostatni wybór będzie używany zarówno dla listy SQL SELECT, jak i dla hydratacji obiektu po pobraniu wierszy.

1

dla W skrócie: część twojego pytania: odpowiedź brzmi "tak".
tutaj jest resharpers spowodować

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

Ale czy to nie dodaje jeszcze jednego "anonimowego" stworzenia w porównaniu do drugiego stwierdzenia, czy też się tutaj mylę?

Jak pokazuje odpowiedź dotctor: oto, co robi kompilator podczas używania składni ze zrozumieniem w tym przypadku. Uwzględniając rok w pojedynczym anonimowym typie, zmniejszasz nieznacznie narzut.

Jednakże:

  • anonimowych typy są bardzo lekkie: stosowanie leków generycznych i generycznych że instancja nad typami referencyjnymi dzielić wdrożenie oznacza, że ​​jest trochę kodu (przyjrzeć montażu w dekompilator).
  • Podczas gdy wiele instancji jest tworzonych, w większości przypadków będą one czyszczone w generacji 0, ponieważ są wydawane niemal natychmiast.
  • Optymalizatory kompilatorów C# i JIT mają wiele do zaoferowania. Jest całkiem możliwe, że skróty są podejmowane (ale musisz przeczytać zestaw x86/x64 z uruchomionego procesu, aby zobaczyć).

Zapamiętaj dwa pierwsze rules of optimisation: chyba że możesz pokazać - dane profilera z realistycznych danych testowych - masz problem z wydajnością, skupiając się na czytelnym kodzie, który jest łatwy w utrzymaniu.

3

Inną alternatywą byłoby przenieść metodę where przed metody join a następnie pozostawić rok z anonimowego Klasa:

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(); 

ale w ogóle inne odpowiedzi rację, że nie ma zbyt dużej różnicy i kompilator może obsłużyć szczegóły dla ciebie.

Powiązane problemy