Codice Linq per selezionare un elemento


105

Mi ritrovo a scrivere molto codice come questo per selezionare un elemento che corrisponde

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

C'è un modo più pulito per farlo o è così conciso come vedrò?

EDIT: Avrebbe dovuto dire "Modo più pulito usando la sintassi di linq". Ero già a conoscenza della sintassi lambda e sta iniziando a sembrare che questo sia in realtà l'unico modo. Tuttavia, ho ricevuto alcune informazioni utili, quindi grazie a tutti coloro che hanno risposto.


5
Personalmente evito Single()e SingleOrDefault()SE so che i dati sono già univoci (ad esempio da un database che ha quel vincolo, ecc.), Poiché lo Single()costringo a scansionare il resto della lista per trovare un possibile duplicato, ma sono io. Se a questo punto hai bisogno di rafforzare la tua unicità, usa la Single()famiglia, altrimenti usa la First()famiglia.
James Michael Hare

Risposte:


176

Dipende da quanto ti piace la sintassi delle query di linq, puoi usare direttamente i metodi di estensione come:

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

E se non vuoi generare un errore se l'elenco è vuoto, usa FirstOrDefaultche restituisce il valore predefinito per il tipo di elemento ( nullper i tipi di riferimento):

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

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

Single()e SingleOrDefault()può anche essere usato, ma se stai leggendo da un database o qualcosa che già garantisce l'unicità non mi preoccuperei perché deve scansionare l'elenco per vedere se ci sono duplicati e lanci. First()e si FirstOrDefault()fermano alla prima partita, quindi sono più efficienti.

Della famiglia First()e Single(), ecco dove lanciano:

  • First() - lancia se vuoto / non trovato, non lancia se duplicato
  • FirstOrDefault() - restituisce il default se vuoto / non trovato, non lancia se duplicato
  • Single() - lancia se vuoto / non trovato, lancia se esiste un duplicato
  • SingleOrDefault() - restituisce il valore predefinito se vuoto / non trovato, lancia se esiste un duplicato

1
Penso che ti manchino due segni di uguale lì. Dovrebbe esserei.Id == 123
davehale23

18

FirstOrDefault o SingleOrDefault potrebbero essere utili, a seconda del tuo scenario e se vuoi gestire la presenza di zero o più di una corrispondenza:

FirstOrDefault: restituisce il primo elemento di una sequenza o un valore predefinito se non viene trovato alcun elemento.

SingleOrDefault: restituisce l'unico elemento di una sequenza o un valore predefinito se la sequenza è vuota; questo metodo genera un'eccezione se c'è più di un elemento nella sequenza

Non so come funzioni in una query linq 'from' ma nella sintassi lambda assomiglia a questo:

var item1 = Items.FirstOrDefault(x => x.Id == 123);
var item2 = Items.SingleOrDefault(x => x.Id == 123);

qual è il più efficiente in termini di tempo DB? Se avessi un varchar, volevo una corrispondenza esatta ma possibile che non ce ne fosse?
Piotr Kula

2
La risposta accettata risolve questo problema in modo completo, ma essenzialmente FirstOrDefault si interrompe non appena trova una corrispondenza, ma SingleOrDefault deve esaminare l'intero elenco per assicurarsi che esista esattamente una corrispondenza.
stuartd

12

Questi sono i metodi preferiti:

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

O

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

Grazie, quindi in questa situazione non c'è la notazione linq e devo usare lambda?
Mikey Hogarth

È piuttosto buono. Non ho usato molto linq, quindi non sono nemmeno sicuro di essere a conoscenza di questi metodi.
wageoghe

1
Il metodo singolo verifica che il valore restituito sia univoco. Quindi, se la tua collezione è grande, possono volerci molte volte. Il primo metodo restituisce solo il primo elemento che corrisponde al predicato.
meziantou

La risposta di @wageoghe James non usa linq: i metodi Single e SingleOrDefault fanno parte dell'implementazione di IEnumerable.
Mikey Hogarth

12

Solo per semplificare la vita di qualcuno, la query linq con espressione lambda

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

risulta in una query SQL con select top (1)dentro.


9

Questo può essere condensato meglio in questo.

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

La tua query sta attualmente raccogliendo tutti i risultati (e potrebbero essercene più di uno) all'interno dell'enumerabile e quindi sta prendendo il primo da quel set, facendo più lavoro del necessario.

Single / SingleOrDefault sono utili, ma solo se desideri scorrere l'intera raccolta e verificare che la corrispondenza sia unica oltre a selezionare quella corrispondenza. First / FirstOrDefault prenderà solo la prima corrispondenza e se ne andrà, indipendentemente dal numero di duplicati effettivamente esistenti.


4

È possibile utilizzare la sintassi del metodo di estensione:

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

Oltre a questo, non sono sicuro di quanto si possa ottenere in modo più conciso, senza magari scrivere i propri metodi di estensione "First" e "FirstOrDefault" specializzati.


Non credo che questo sia il comportamento previsto. Questa istruzione Select restituisce (in realtà non lo fanno, ma seleziona per return) una raccolta di bool, uno per ogni elemento di Items, il primo di ciò che viene restituito come elemento, che diventa semplicemente un bool. Usa la soluzione di Chris Hannon
Leonardo Daga

Forse intendi Whereal contrario di Select, che è già stato affermato, ma questa risposta non è corretta. Seleziona in c # cambia i risultati in IEnumerable <bool>, quindi boolx.Id == 123
ottieni

2

Ti dirò cosa ha funzionato per me:

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;

Il mio id è la riga che voglio interrogare, in questo caso l'ho preso da un radGrid, poi l'ho usato per interrogare, ma questa query restituisce una riga, quindi puoi assegnare i valori che hai ottenuto dalla query alla casella di testo, o qualsiasi altra cosa , Ho dovuto assegnarli alla casella di testo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.