Odpowiedzi:
Wiem, że inni napisali, dlaczego używasz jednego lub drugiego, ale pomyślałem, że zilustruję, dlaczego NIE powinieneś używać jednego, gdy masz na myśli drugie.
Uwaga: W moim kodu, ja zazwyczaj korzystają FirstOrDefault()
i SingleOrDefault()
, ale to już inna kwestia.
Weźmy na przykład tabelę, która przechowuje Customers
w różnych językach przy użyciu klucza złożonego ( ID
, Lang
):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).First();
Powyższy kod wprowadza możliwy błąd logiczny (trudny do prześledzenia). Zwróci więcej niż jeden rekord (zakładając, że masz rekord klienta w wielu językach), ale zawsze zwróci tylko pierwszy rekord ... który może czasem działać ... ale nie inne. To nieprzewidywalne.
Ponieważ Twoim celem jest zwrot jednorazowego Customer
użytku Single()
;
Następujące wywołałoby wyjątek (co w tym przypadku jest potrzebne):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).Single();
Następnie po prostu uderzasz się w czoło i mówisz do siebie ... OOPS! Zapomniałem pola językowego! Oto poprawna wersja:
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 && c.Lang == "en" ).Single();
First()
jest przydatny w następującym scenariuszu:
DBContext db = new DBContext();
NewsItem newsitem = db.NewsItems.OrderByDescending( n => n.AddedDate ).First();
Zwróci JEDEN obiekt, a ponieważ używasz sortowania, zostanie zwrócony najnowszy rekord.
Używanie, Single()
gdy czujesz, że powinno to wyraźnie zwracać 1 rekord, pomoże ci uniknąć błędów logicznych.
customers.Where(predicate).Single()
customers.Single(predicate)
?
Single wyrzuci wyjątek, jeśli znajdzie więcej niż jeden rekord spełniający kryteria. Najpierw zawsze wybierze pierwszy rekord z listy. Jeśli zapytanie zwraca tylko 1 rekord, możesz przejść do First()
.
Oba zgłoszą InvalidOperationException
wyjątek, jeśli kolekcja jest pusta. Alternatywnie możesz użyć SingleOrDefault()
. Nie spowoduje to wyrzucenia wyjątku, jeśli lista jest pusta
Pojedynczy()
Zwraca pojedynczy określony element zapytania
Kiedy używać : Jeśli dokładnie 1 element jest oczekiwany; nie 0 lub więcej niż 1. Jeśli lista jest pusta lub zawiera więcej niż jeden element, wygeneruje wyjątek „Sekwencja zawiera więcej niż jeden element”
SingleOrDefault ()
Zwraca pojedynczy określony element zapytania lub wartość domyślną, jeśli nie znaleziono żadnego wyniku
When Use : Gdy oczekiwanych jest 0 lub 1 elementów. Zgłasza wyjątek, jeśli lista zawiera 2 lub więcej elementów.
Pierwszy()
Zwraca pierwszy element zapytania z wieloma wynikami.
Gdy używasz : gdy spodziewany jest 1 lub więcej elementów i chcesz tylko pierwszy. Zgłasza wyjątek, jeśli lista nie zawiera żadnych elementów.
FirstOrDefault ()
Zwraca pierwszy element listy z dowolną ilością elementów lub wartością domyślną, jeśli lista jest pusta.
When Use : Gdy oczekuje się wielu elementów i chcesz tylko pierwszy. Lub lista jest pusta i potrzebujesz domyślnej wartości dla określonego typu, takiej samej jak
default(MyObjectType)
. Na przykład: jeśli typ listylist<int>
to zwróci pierwszą liczbę z listy lub 0, jeśli lista jest pusta. Jeśli taklist<string>
, zwróci pierwszy ciąg z listy lub null, jeśli lista jest pusta.
First
gdy spodziewany jest 1 lub więcej elementów , nie tylko „więcej niż 1” i FirstOrDefault
dowolna ilość elementów.
Między tymi dwiema metodami istnieje subtelna, semantyczna różnica.
Służy Single
do pobierania pierwszego (i jedynego) elementu z sekwencji, która powinna zawierać jeden element i nie więcej. Jeśli sekwencja zawiera więcej niż jeden element, twoje wywołanie Single
spowoduje zgłoszenie wyjątku, ponieważ wskazałeś, że powinien być tylko jeden element.
Służy First
do pobierania pierwszego elementu z sekwencji, która może zawierać dowolną liczbę elementów. Jeśli sekwencja zawiera więcej niż jeden element, twoje wywołanieFirst
nie spowoduje wygenerowania wyjątku, ponieważ wskazałeś, że potrzebujesz tylko pierwszego elementu w sekwencji i nie obchodzi cię, czy istnieje więcej.
Jeśli sekwencja nie zawiera żadnych elementów, oba wywołania metod spowodują zgłoszenie wyjątków, ponieważ obie metody oczekują obecności co najmniej jednego elementu.
Jeśli nie chcesz specjalnie zgłaszać wyjątku w przypadku, gdy jest więcej niż jeden element, użyjFirst()
.
Oba są wydajne, weź pierwszy przedmiot. First()
jest nieco bardziej wydajny, ponieważ nie zawraca sobie głowy sprawdzaniem, czy jest drugi element.
Jedyną różnicą jest to, że Single()
oczekuje się, że na początku będzie tylko jeden element w wyliczeniu, i zgłosi wyjątek, jeśli istnieje więcej niż jeden. Państwo skorzystać .Single()
jeśli chcemy specjalnie rzucony wyjątek w tym przypadku.
O ile pamiętam, Single () sprawdza, czy po pierwszym jest inny element (i rzuca wyjątek, jeśli tak jest), podczas gdy First () zatrzymuje się po otrzymaniu. Oba zgłaszają wyjątek, jeśli sekwencja jest pusta.
Osobiście zawsze używam First ().
Jeśli chodzi o wydajność: współpracowaliśmy z współpracownikiem nad wydajnością Single vs First (lub SingleOrDefault vs FirstOrDefault) i argumentowałem za tym, aby First (lub FirstOrDefault) był szybszy i poprawić wydajność (chodzi mi o stworzenie naszej aplikacji Biegnij szybciej).
Przeczytałem kilka postów na temat przepełnienia stosu, które debatują nad tym. Niektórzy twierdzą, że niewielki wzrost wydajności uzyskuje się przy użyciu First zamiast Single. Wynika to z faktu, że First po prostu zwraca pierwszy element, a Single musi zeskanować wszystkie wyniki, aby upewnić się, że nie ma duplikatu (tzn .: jeśli znalazł element w pierwszym wierszu tabeli, nadal skanowałby każdy inny wiersz do upewnij się, że nie ma drugiej wartości pasującej do warunku, który spowodowałby błąd). Czułem, że jestem na solidnym gruncie, ponieważ „First” jest szybszy niż „Single”, więc postanowiłem to udowodnić i odłożyć debatę na później.
Ustawiłem test w mojej bazie danych i dodałem 1 000 000 wierszy ID UniqueIdentifier Foreign UniqueIdentifier Info nvarchar (50) (wypełniony łańcuchami liczb od „0” do „999,9999”
Załadowałem dane i ustawiłem ID jako pole klucza podstawowego.
Korzystając z LinqPad, moim celem było pokazanie, że jeśli szukałeś wartości w „Zagranicznych” lub „Informacyjnych” za pomocą Single, byłoby to znacznie gorsze niż użycie First.
Nie potrafię wyjaśnić wyników, które uzyskałem. W prawie każdym przypadku użycie Single lub SingleOrDefault było nieco szybsze. Nie ma to dla mnie żadnego logicznego sensu, ale chciałem się tym podzielić.
Np .: Użyłem następujących zapytań:
var q = TestTables.First(x=>x.Info == "314638") ;
//Vs.
Var q = TestTables.Single(x=>x.Info =="314638") ; //(this was slightly faster to my surprise)
Próbowałem podobnych zapytań w polu klucza „Zagraniczne”, które nie zostały zaindeksowane, by udowodnić, że First jest szybszy, ale Single był zawsze nieco szybszy w moich testach.
Oni są różni. Oba twierdzą, że zestaw wyników nie jest pusty, ale single potwierdza również, że nie ma więcej niż 1 wynik. Osobiście używam Single w przypadkach, w których spodziewam się, że będzie tylko 1 wynik tylko dlatego, że odzyskanie więcej niż 1 wyniku jest błędem i prawdopodobnie powinno być traktowane jako takie.
Możesz wypróbować prosty przykład, aby uzyskać różnicę. Wyjątek zostanie zgłoszony w wierszu 3;
List<int> records = new List<int>{1,1,3,4,5,6};
var record = records.First(x => x == 1);
record = records.Single(x => x == 1);
Zapisy w podmiocie pracowniczym:
Employeeid = 1
: Tylko jeden pracownik z tym identyfikatorem
Firstname = Robert
: Więcej niż jeden pracownik o tym nazwisku
Employeeid = 10
: Brak pracownika o tym identyfikatorze
Teraz trzeba dokładnie zrozumieć, co Single()
i co First()
oznacza.
Pojedynczy()
Funkcja Single () służy do zwracania pojedynczego rekordu, który wyjątkowo występuje w tabeli, dlatego poniższe zapytanie zwróci pracownika, którego employeed =1
ponieważ mamy tylko jednego pracownika, który Employeed
ma 1. Jeśli mamy dwa rekordy, EmployeeId = 1
wówczas generuje błąd (patrz błąd poniżej w drugim zapytaniu, w którym wykorzystujemy przykład Firstname
.
Employee.Single(e => e.Employeeid == 1)
Powyższe zwróci pojedynczy rekord, który ma 1 employeeId
Employee.Single(e => e.Firstname == "Robert")
Powyższe spowoduje zgłoszenie wyjątku, ponieważ w tabeli znajdują się rekordy wielokrotne FirstName='Robert'
. Wyjątkiem będzie
InvalidOperationException: Sequence zawiera więcej niż jeden element
Employee.Single(e => e.Employeeid == 10)
To ponownie wyrzuci wyjątek, ponieważ nie istnieje rekord dla id = 10. Wyjątkiem będzie
InvalidOperationException: Sequence nie zawiera elementów.
Gdyż EmployeeId = 10
zwróci wartość null, ale gdy Single()
go używamy , wygeneruje błąd. Aby obsłużyć błąd zerowy, powinniśmy użyć SingleOrDefault()
.
Pierwszy()
First () zwraca z wielu rekordów odpowiadające im rekordy posortowane w porządku rosnącym zgodnie z, birthdate
więc zwróci „Robert”, który jest najstarszy.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Firstname == "Robert")
Powyżej powinien zwrócić najstarszy, Robert zgodnie z DOB.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Employeeid == 10)
Powyżej wyrzuci wyjątek, ponieważ nie istnieje rekord dla id = 10. Aby uniknąć wyjątku zerowego, powinniśmy FirstOrDefault()
raczej użyć First()
.
Uwaga: Możemy użyć tylko First()
/ Single()
gdy jesteśmy absolutnie pewni, że nie może zwrócić wartości zerowej.
W obu funkcjach użyj SingleOrDefault () OR FirstOrDefault (), który obsłuży wyjątek zerowy, w przypadku braku znalezionego rekordu zwróci null.