LINQ:
Czy bardziej efektywne jest użycie Single()
operatora, First()
gdy wiem, że zapytanie zwróci pojedynczy rekord ?
Czy jest jakaś różnica?
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źródło
Pojedynczy()
SingleOrDefault ()
Pierwszy()
FirstOrDefault ()
źródło
First
gdy spodziewany jest 1 lub więcej elementów , nie tylko „więcej niż 1” iFirstOrDefault
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łanieSingle
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.
źródło
Jeśli nie chcesz specjalnie zgłaszać wyjątku w przypadku, gdy jest więcej niż jeden element, użyj
First()
.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.źródło
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 ().
źródło
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ń:
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.
źródło
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.
źródło
Możesz wypróbować prosty przykład, aby uzyskać różnicę. Wyjątek zostanie zgłoszony w wierszu 3;
źródło
Wiele osób, które znam, korzysta z FirstOrDefault (), ale częściej używam SingleOrDefault (), ponieważ często byłoby więcej niespójności danych, gdyby było więcej niż jeden. Dotyczy to jednak LINQ-to-Objects.
źródło
Zapisy w podmiocie pracowniczym:
Employeeid = 1
: Tylko jeden pracownik z tym identyfikatoremFirstname = Robert
: Więcej niż jeden pracownik o tym nazwiskuEmployeeid = 10
: Brak pracownika o tym identyfikatorzeTeraz trzeba dokładnie zrozumieć, co
Single()
i coFirst()
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óryEmployeed
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ładFirstname
.Powyższe zwróci pojedynczy rekord, który ma 1
employeeId
Powyższe spowoduje zgłoszenie wyjątku, ponieważ w tabeli znajdują się rekordy wielokrotne
FirstName='Robert'
. Wyjątkiem będzieTo ponownie wyrzuci wyjątek, ponieważ nie istnieje rekord dla id = 10. Wyjątkiem będzie
Gdyż
EmployeeId = 10
zwróci wartość null, ale gdySingle()
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.Powyżej powinien zwrócić najstarszy, Robert zgodnie z DOB.
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.
źródło