LINQ: kiedy używać SingleOrDefault vs. FirstOrDefault () z kryteriami filtrowania

Rozważmy metody rozszerzenia IEnumerable SingleOrDefault() i FirstOrDefault()

MSDN dokumentuje, że SingleOrDefault:

Zwraca jedyny element sekwencji lub wartość domyślną, jeśli sekwencja jest pusta; metoda ta rzuca wyjątek, jeśli w sekwencji znajduje się więcej niż jeden element.

FirstOrDefault z MSDN (prawdopodobnie przy użyciu OrderBy() lub OrderByDescending() lub wcale),

Zwraca pierwszy element Sekwencja

Rozważmy kilka przykładowych zapytań, nie zawsze jest jasne, kiedy użyć tych dwóch metod:

var someCust = db.Customers
.SingleOrDefault(c=>c.ID == 5); //unlikely(?) to be more than one, but technically COULD BE

var bobbyCust = db.Customers
.FirstOrDefault(c=>c.FirstName == "Bobby"); //clearly could be one or many, so use First?

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or does it matter?

Pytanie

Jakie konwencje stosujesz lub sugerujesz decydując się na użycie SingleOrDefault() i FirstOrDefault() w zapytaniach LINQ?

Author: p.campbell, 2009-11-17

13 answers

Za każdym razem, gdy używasz SingleOrDefault, wyraźnie stwierdzasz, że zapytanie powinno skutkować co najwyżej pojedynczym wynikiem. Z drugiej strony, gdy używane jest FirstOrDefault, zapytanie może zwrócić dowolną ilość wyników, ale użytkownik oświadcza, że chce tylko pierwszy.

Osobiście uważam, że semantyka jest bardzo różna i użycie odpowiedniej, w zależności od oczekiwanych rezultatów, poprawia czytelność.

 376
Author: Bryan Menard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-17 00:04:24

Jeśli zestaw wyników zwróci 0 rekordów:

  • SingleOrDefault zwraca wartość domyślną dla typu (np. wartość domyślna dla int to 0)
  • FirstOrDefault Zwraca wartość domyślną dla typu

Jeśli ustawisz wynik zwróci 1 rekord:

  • SingleOrDefault zwraca ten rekord
  • FirstOrDefault zwraca ten rekord

Jeśli zestaw wyników zwróci wiele rekordów:

  • SingleOrDefault rzuca wyjątek
  • FirstOrDefault zwraca pierwszy rekord

Wniosek:

Jeśli chcesz, aby wyjątek został wyrzucony, jeśli zestaw wyników zawiera wiele rekordów, użyj SingleOrDefault.

Jeśli zawsze chcesz 1 rekord bez względu na to, co zawiera zestaw wyników, użyj FirstOrDefault

 491
Author: Alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-04 04:03:24

Jest

  • różnica semantyczna
  • różnica wydajności

Między nimi.

Różnica Semantyczna:

  • FirstOrDefault zwraca pierwszą pozycję potencjalnie wielokrotną(lub domyślną, jeśli nie istnieje).
  • SingleOrDefault zakłada, że istnieje pojedynczy element i zwraca go (lub domyślnie, jeśli żaden nie istnieje). Wiele przedmiotów jest naruszeniem umowy, wyjątek jest wyrzucany.

Osiągi Różnica

  • FirstOrDefault zwykle jest szybszy, iterauje aż znajdzie element i musi tylko iterować całość, gdy go nie znajdzie. W wielu przypadkach istnieje duże prawdopodobieństwo znalezienia przedmiotu.

  • SingleOrDefault musi sprawdzić, czy jest tylko jeden element i dlatego zawsze iteruje całą liczbę. Aby być precyzyjnym, iteracja trwa do momentu znalezienia drugiego elementu i wyrzucenia wyjątku. Ale w większości przypadków nie ma drugiego element.

Wniosek

  • Użyj FirstOrDefault, jeśli nie obchodzi cię, ile przedmiotów jest lub , gdy nie możesz sobie pozwolić na sprawdzenie wyjątkowości (np. w bardzo dużej kolekcji). Po sprawdzeniu wyjątkowości dodawania przedmiotów do kolekcji może być zbyt kosztowne, aby sprawdzić je ponownie podczas wyszukiwania tych przedmiotów.

  • Użyj SingleOrDefault, jeśli nie musisz zbytnio dbać o wydajność i chcesz mieć pewność, że założenie pojedynczego elementu jest czytelny dla czytnika i sprawdzany w czasie wykonywania.

W praktyce używa sięFirst / FirstOrDefault często nawet w przypadkach, gdy zakładasz pojedynczy przedmiot, w celu poprawy wydajności. Należy pamiętać, że Single / SingleOrDefault może poprawić czytelność (ponieważ stwierdza założenie pojedynczego elementu) i stabilność (ponieważ sprawdza go) i używać go odpowiednio.

 214
Author: Stefan Steinegger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-19 09:33:42

Nikt nie wspomniał, że FirstOrDefault przetłumaczone w SQL robi TOP 1 rekord, A SingleOrDefault robi TOP 2, ponieważ musi wiedzieć, czy jest więcej niż 1 rekord.

 61
Author: shalke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-06 12:55:00

For LINQ - > SQL:

SingleOrDefault

  • wygeneruje zapytanie typu "select * from users where userid = 1"
  • Wybierz pasujący rekord, rzuca wyjątek, jeśli znaleziono więcej niż jeden rekord
  • użyj, jeśli pobierasz dane w oparciu o kolumnę klucza podstawowego / unikalnego

FirstOrDefault

  • wygeneruje zapytanie typu "select top 1 * from users where userid = 1"
  • Wybierz pierwsze pasujące wiersze
  • użyj jeśli pobieranie danych odbywa się w oparciu o kolumnę non primary / unique key
 9
Author: Prakash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-04 13:35:16

Używam SingleOrDefault w sytuacjach, w których moja logika nakazuje, że wynik będzie zerowy lub jeden. Jeśli jest ich więcej, jest to sytuacja błędu, która jest pomocna.

 7
Author: spender,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-17 00:05:25

SingleOrDefault: mówisz, że "co najwyżej" jest jeden element pasujący do zapytania lub domyślny Po pierwsze: mówisz, że istnieje" co najmniej " jeden element pasujący do zapytania lub domyślny

Powiedz to na głos następnym razem, gdy będziesz musiał wybrać, a prawdopodobnie wybierzesz mądrze. :)

 5
Author: Steven,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-01 16:22:34

W Twoich przypadkach użyłbym:

Select by ID==5: jest OK, aby używać SingleOrDefault tutaj, ponieważ oczekujesz jednego [lub żaden] encji, jeśli masz więcej niż jeden encji z ID 5, jest coś nie tak i zdecydowanie wyjątek godny.

Podczas wyszukiwania osób, których imię jest równe" Bobby", może być więcej niż jeden (całkiem możliwe, że tak myślę), więc nie powinieneś używać Single ani First, po prostu wybierz z operacji Where (jeśli "Bobby" zwraca zbyt wiele encji, użytkownik musi zawęzić wyszukiwanie lub wybrać jeden ze zwracanych wyników)

Kolejność według daty utworzenia powinna być również wykonywana za pomocą operacji Where (mało prawdopodobne, aby mieć tylko jeden encja, sortowanie nie byłoby zbyt przydatne ;) oznacza to jednak, że chcesz posortować wszystkie encje - jeśli chcesz tylko jeden, użyj FirstOrDefault, Single rzuciłby za każdym razem, jeśli masz więcej niż jeden encja.

 4
Author: Olli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-17 00:12:07

W ostatnim przykładzie:

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or doesn't matter?
Tak. Jeśli spróbujesz użyć SingleOrDefault(), A zapytanie spowoduje więcej niż rekord, otrzymasz wyjątek i wyjątek. Możesz bezpiecznie używać SingleOrDefault() tylko wtedy, gdy spodziewasz się tylko 1 i tylko 1 wyniku...
 3
Author: bytebender,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-17 00:07:35

Oba są operatorami elementu i są używane do wybierania pojedynczego elementu z sekwencji. Ale jest między nimi niewielka różnica. Operator SingleOrDefault() wyrzuci wyjątek, jeśli więcej niż jeden element jest spełniony warunek, w którym jako FirstOrDefault () nie wyrzuci żadnego wyjątku dla tego samego. Oto przykład.

List<int> items = new List<int>() {9,10,9};
//Returns the first element of a sequence after satisfied the condition more than one elements
int result1 = items.Where(item => item == 9).FirstOrDefault();
//Throw the exception after satisfied the condition more than one elements
int result3 = items.Where(item => item == 9).SingleOrDefault();
 3
Author: Sheo Dayal Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-20 08:34:52

Tak jak teraz rozumiem, SingleOrDefault będzie dobre, jeśli zapytasz o dane, które są gwarantowane jako unikalne, tj. wymuszone przez ograniczenia DB, takie jak klucz podstawowy.

Czy istnieje lepszy sposób zapytań o klucz podstawowy.

Zakładając, że mój stół ma

AccountNumber - Primary Key, integer
AccountName
AccountOpenedDate
AccountIsActive
etc.

I chcę zapytać o AccountNumber 987654, używam

var data = datacontext.TableAcc.FirstOrDefault(obj => obj.AccountNumber == 987654);
 1
Author: M G,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-05 14:05:42

Jedna rzecz, której brakuje w odpowiedziach....

Jeśli jest wiele wyników, FirstOrDefault bez zlecenia By może przywrócić różne wyniki w zależności od tego, która strategia indeksowania kiedykolwiek została użyta przez serwer.

Osobiscie nie moge znosic pierwszego wejscia w kod bo dla mnie pisze ze programista nie dbal o wyniki. Z zamówieniem przez choć może być przydatny jako sposób egzekwowania najnowszego / najwcześniejszego. Musiałem naprawić wiele problemów spowodowanych przez nieostrożni Programiści korzystający z FirstOrDefault.

 0
Author: El Dude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-11 22:39:17

Nie rozumiem, dlaczego używasz FirstOrDefault(x=> x.ID == key), kiedy to może szybciej pobierać wyniki, jeśli używasz Find(key). Jeśli zapytujesz za pomocą klucza głównego tabeli, regułą jest zawsze używanie Find(key). FirstOrDefault powinny być używane do predykatów takich jak (x=> x.Username == username) itp.

To nie zasługiwało na negatywną ocenę, ponieważ nagłówek pytania nie był specyficzny dla linq on DB lub Linq to List / IEnumerable itp.

 -7
Author: Theron Govender,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-29 09:32:40