Kod Linq, aby wybrać jedną pozycję

105

Piszę dużo takiego kodu, aby wybrać jeden pasujący element

var item = (from x in Items where x.Id == 123 select x).First();

Czy jest na to czystszy sposób, czy jest to tak zwięzłe, jak zamierzam?

EDYCJA: Powinienem był powiedzieć „Czystszy sposób korzystania ze składni linq”. Byłem już świadomy składni lambda i zaczyna wyglądać, że to właściwie jedyny sposób. Dostałem jednak przydatne informacje, więc dziękuję wszystkim, którzy odpowiedzieli.

Mikey Hogarth
źródło
5
Osobiście unikam Single()i SingleOrDefault()JEŚLI wiem, że dane są już unikalne (na przykład z bazy danych, która ma to ograniczenie itp.), Ponieważ Single()zmusza ją do przeszukania reszty listy w celu znalezienia możliwego duplikatu, ale to ja. Jeśli w tym momencie musisz narzucić swoją wyjątkowość, użyj Single()rodziny, jeśli nie, użyj First()rodziny.
James Michael Hare

Odpowiedzi:

176

W zależności od tego, jak bardzo podoba Ci się składnia zapytania linq, możesz użyć metod rozszerzających bezpośrednio, takich jak:

var item = Items.First(i => i.Id == 123);

A jeśli nie chcesz zgłaszać błędu, jeśli lista jest pusta, użyj funkcji, FirstOrDefaultktóra zwraca domyślną wartość dla typu elementu ( nulldla typów referencyjnych):

var item = Items.FirstOrDefault(i => i.Id == 123);

if (item != null)
{
    // found it
}

Single()i SingleOrDefault()może być również używany, ale jeśli czytasz z bazy danych lub czegoś, co już gwarantuje wyjątkowość, nie przejmowałbym się, ponieważ musi skanować listę, aby sprawdzić, czy są jakieś duplikaty i wyrzuty. First()i FirstOrDefault()zatrzymają się na pierwszym meczu, dzięki czemu są bardziej wydajni.

Z rodziny First()i Single(), oto gdzie rzucają:

  • First() - wyrzuca, jeśli jest pusty / nie znaleziony, nie rzuca, jeśli jest zduplikowany
  • FirstOrDefault() - zwraca wartość domyślną, jeśli jest pusta / nie znaleziono, nie rzuca, jeśli jest zduplikowana
  • Single() - wyrzuca, jeśli jest pusty / nie znaleziono, wyrzuca, jeśli istnieje duplikat
  • SingleOrDefault() - zwraca wartość domyślną, jeśli jest pusta / nie znaleziono, zgłasza, jeśli istnieje duplikat
James Michael Hare
źródło
1
Myślę, że brakuje tam dwóch znaków równości. Powinno byći.Id == 123
davehale23
18

FirstOrDefault lub SingleOrDefault mogą być przydatne, w zależności od scenariusza i tego, czy chcesz obsłużyć zero lub więcej dopasowań:

FirstOrDefault: zwraca pierwszy element sekwencji lub wartość domyślną, jeśli nie zostanie znaleziony żaden element.

SingleOrDefault: zwraca jedyny element sekwencji lub wartość domyślną, jeśli sekwencja jest pusta; ta metoda zgłasza wyjątek, jeśli w sekwencji jest więcej niż jeden element

Nie wiem, jak to działa w zapytaniu linq „from”, ale w składni lambda wygląda to tak:

var item1 = Items.FirstOrDefault(x => x.Id == 123);
var item2 = Items.SingleOrDefault(x => x.Id == 123);
stuartd
źródło
który z nich jest najbardziej efektywny pod względem czasu DB? Gdybym miał varchar, chciałem dokładnego dopasowania, ale możliwe, że nie ma takiego?
Piotr Kula
2
Zaakceptowana odpowiedź w pełni rozwiązuje ten problem, ale zasadniczo FirstOrDefault zatrzymuje się, gdy tylko znajdzie dopasowanie, ale SingleOrDefault musi zbadać całą listę, aby upewnić się, że istnieje dokładnie jedno dopasowanie.
stuartd
12

Oto preferowane metody:

var item = Items.SingleOrDefault(x => x.Id == 123);

Lub

var item = Items.Single(x => x.Id == 123);
James Hill
źródło
Dzięki - czy w tej sytuacji nie ma notacji linq i muszę używać lambd?
Mikey Hogarth
To całkiem nieźle. Tak naprawdę nie korzystałem zbyt często z Linq, więc nie jestem pewien, czy byłem nawet świadomy tych metod.
wageoghe
1
Metoda pojedyncza sprawdza, czy zwracana wartość jest unikalna. Więc jeśli twoja kolekcja jest duża, może to zająć dużo czasu. Pierwsza metoda po prostu zwraca pierwszy element, który pasuje do predykatu.
meziantou
Odpowiedź @wageoghe Jamesa nie używa linq - metody Single i SingleOrDefault są częścią implementacji IEnumerable.
Mikey Hogarth
12

Aby ułatwić komuś życie, zapytanie linq z wyrażeniem lambda

(from x in Items where x.Id == 123 select x).FirstOrDefault();

powoduje zapytanie SQL zawierające select top (1)w sobie.

Amal
źródło
9

Można to lepiej sprowadzić do tego.

var item = Items.First(x => x.Id == 123);

Twoje zapytanie zbiera obecnie wszystkie wyniki (a może być więcej niż jeden) w ramach wyliczenia, a następnie pobiera pierwszy z tego zestawu, wykonując więcej pracy niż to konieczne.

Single / SingleOrDefault są warte zachodu, ale tylko wtedy, gdy chcesz wykonać iterację przez całą kolekcję i sprawdzić, czy dopasowanie jest unikalne, oprócz wybrania tego dopasowania. First / FirstOrDefault po prostu weźmie pierwsze dopasowanie i opuści, niezależnie od tego, ile faktycznie duplikatów istnieje.

Chris Hannon
źródło
4

Możesz użyć składni metody rozszerzenia:

var item = Items.Select(x => x.Id == 123).FirstOrDefault();

Poza tym nie jestem pewien, o ile bardziej zwięźle można uzyskać bez pisania własnych wyspecjalizowanych metod rozszerzających „First” i „FirstOrDefault”.

wageoghe
źródło
Nie sądzę, żeby było to zamierzone zachowanie. Ta instrukcja Select zwraca (właściwie tego nie robi, ale wybiera do zwrotu) zbiór bool, po jednym dla każdego elementu Items, pierwszego z tego, co jest zwracane jako element, który staje się po prostu wartością logiczną. Skorzystaj z rozwiązania Chrisa Hannona
Leonardo Dagi
Być może masz na myśli Wherew przeciwieństwie do tego Select, co już zostało powiedziane, ale ta odpowiedź jest nieprawidłowa. Wybierz w c # zmienia wyniki na IEnumerable <bool>, więc otrzymujesz booldla pierwszego elementux.Id == 123
bradlis7
2

Powiem ci, co mi pomogło:

int id = int.Parse(insertItem.OwnerTableView.DataKeyValues[insertItem.ItemIndex]["id_usuario"].ToString());

var query = user.First(x => x.id_usuario == id);
tbUsername.Text = query.username;
tbEmail.Text = query.email;
tbPassword.Text = query.password;

Mój identyfikator to wiersz, który chcę zapytać, w tym przypadku otrzymałem go z radGrid, a następnie użyłem go do zapytania, ale to zapytanie zwraca wiersz, a następnie możesz przypisać wartości otrzymane z zapytania do pola tekstowego lub cokolwiek , Musiałem je przypisać do pola tekstowego.

G Jeny Ramirez
źródło