2012-05-04 7 views
29

Realizujemy kilka repozytoriów danych EF i mamy kilka pytań, które zawierają TOP 1
Z Entity Framework lepiej jest użyć .First() lub .Take (1) dla "TOP 1"?

Czytałem wiele postów sugerujące użycie .Take(1)
Kodeksu jestem przeglądu zastosowań .First()

Rozumiem że oba te elementy dają ten sam wynik dla przypisania obiektu, ale czy oba rzeczywiście odpowiadają na to samo zapytanie? Kiedy DB jest zapytany, czy będzie on w rzeczywistości z TOP 1 dla obu wniosków? Czy też wykonają zapytanie w całości w przeliczalne, po prostu weź pierwszą pozycję w kolekcji?

Ponadto, jeśli użyliśmy .FirstOrDefault(), dlaczego mielibyśmy oczekiwać innego zachowania? Wiem, że podczas korzystania z IEnumerable, wywołanie .First() na pustej kolekcji będzie rzucać, ale jeśli to jest tak naprawdę zmienia tylko kwerendę obejmującą TOP 1 wtedy nie powinienem oczekiwać absolutnie żadnej różnicy funkcjonalnej między .First() i .FirstOrDefault() .... prawda?

Czy istnieje pewna lepsza metoda niż te zakresy liczbowe dla wykonania kwerendy wykonującej TOP 1?

+0

Jak zmienić zapytanie ".Take (1)" w obiekt? – Gabe

+0

Użyj 'First()' jeśli chcesz element. Użyj 'Take (1)', jeśli chcesz sekwencji jednego elementu. –

Odpowiedz

53

Z LINQPad:

C#:

age_Centers.Select(c => c.Id).First(); 
age_Centers.Select(c => c.Id).FirstOrDefault(); 
age_Centers.Select(c => c.Id).Take(1).Dump(); 

SQL:

SELECT TOP (1) [t0].[Id] 
FROM [age_Centers] AS [t0] 
GO 

SELECT TOP (1) [t0].[Id] 
FROM [age_Centers] AS [t0] 
GO 

SELECT TOP (1) [t0].[Id] 
FROM [age_Centers] AS [t0] 

* Pamiętaj, że Take(1) wylicza i zwraca IQueryable.

+3

+1 za demonstrację tworzą taki sam wynik zapytania. – Matthew

+3

Co powiecie na query.Skip (10) .Take (1)? Czy te trzy zapytania są takie same? –

5

Przekieruj właściwość DataContext Log do Console.Out lub pliku tekstowego i zobacz, jakie zapytanie tworzy każda opcja.

+3

To prawdopodobnie miało być komentarzem, prawda? – dasblinkenlight

+3

@dasblinkenlight Nie, nie było. Myślę, że to ważna odpowiedź. Opowiadam OP, jak znaleźć odpowiedź sam; z łatwością. – Icarus

+0

Jak mogę to zrobić, gdy moim kontekstem jest edmx? Nie ujawnia właściwości .Log. – Matthew

1

Jak .First() utwory:

Jeśli kolekcja jest typu IList, to pierwszy element jest dostępny od pozycji indeksu, który jest różny w zależności od implementacji zbiórki. W przeciwnym razie iterator zwraca pierwszy element.

I .Take(int count) zawsze iterować.

Jeśli występuje jakikolwiek przyrost, dzieje się tak, gdy kolekcja implementuje IList, a prędkość dostępu do pierwszego elementu przez indeks jest większa niż wartość zwracanego iteratora. Nie wierzę, że to będzie znaczące. ;)

Źródło:

http://www.hookedonlinq.com/FirstOperator.ashx

http://www.hookedonlinq.com/TakeOperator.ashx

+1

Wiem, że w ten sposób '.First()' działa w przeliczalne. Pytam w kontekście tworzonego tutaj zapytania. – Matthew

1

Najpierw pojawi się zapytanie Take 1, więc nie ma różnicy w zapytaniu.Wywołanie FirstOrDefault będzie oświadczeniem jednoetapowym, ponieważ Take zwraca IEnumerable czy będziesz musiał najpierw zadzwonić First.

Najpierw zostanie wyrzucony wyjątek, więc opcja FirstOrDefault jest zawsze preferowana.

I oczywiście ludzie, którzy napisali konwerter kwerend EF, są na tyle sprytni, aby wywołać Take 1 zamiast wykonywania całego zestawu wyników i zwrócenia pierwszego elementu.

Można to sprawdzić za pomocą profilera SQL.

2
**First()** operates on a collection of any number of objects and returns the first object.  **Take(1)** operates on a collection of any number of objects and returns a collection containing the first object. 

Można również użyć jednolitego pojedynczy() działa na zbiorze dokładnie jednego obiektu i po prostu zwraca obiekt.

+0

Powiązane: [LINQ Single vs First] (https://stackoverflow.com/questions/2724096/linq-single-vs-first) – Sinjai