.net - programming - sql server databases download




LINQ: quando utilizzare SingleOrDefault vs. FirstOrDefault() con criteri di filtro (9)

Considerare i metodi di estensione IEnumerable SingleOrDefault() e FirstOrDefault()

MSDN documenta 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.

considerando che FirstOrDefault da MSDN (presumibilmente quando si utilizza un OrderBy() o OrderByDescending() o nessuno affatto),

Restituisce il primo elemento di una sequenza

Considerare una manciata di query di esempio, non è sempre chiaro quando utilizzare questi due metodi:

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?

Domanda

Quali convenzioni segui o suggerisci quando SingleOrDefault() utilizzare SingleOrDefault() e FirstOrDefault() nelle tue query LINQ?


C'è

  • una differenza semantica
  • una differenza di prestazioni

tra i due.

Differenza semantica:

  • FirstOrDefault restituisce un primo elemento potenzialmente multiplo (o predefinito se nessuno esiste).
  • SingleOrDefault presuppone che ci sia un singolo oggetto e lo restituisce (o predefinito se nessuno esiste). Più articoli sono una violazione del contratto, viene generata un'eccezione.

Differenza di prestazioni

  • FirstOrDefault è solitamente più veloce, itera fino a quando non trova l'elemento e deve solo iterare l'intero enumerable quando non lo trova. In molti casi, c'è un'alta probabilità di trovare un oggetto.

  • SingleOrDefault deve verificare se esiste un solo elemento e quindi itera sempre l'intero enumerabile. Per essere precisi, itera fino a quando non trova un secondo elemento e lancia un'eccezione. Ma nella maggior parte dei casi, non c'è un secondo elemento.

Conclusione

  • Usa FirstOrDefault se non ti interessa quanti articoli ci sono o quando non ti puoi permettere di controllare l'unicità (ad esempio in una collezione molto grande). Quando si controlla l'univocità aggiungendo gli elementi alla raccolta, potrebbe essere troppo costoso controllarlo di nuovo durante la ricerca di tali elementi.

  • Usa SingleOrDefault se non ti interessa troppo le prestazioni e vuoi assicurarti che l'assunzione di un singolo elemento sia chiara per il lettore e verificata in fase di runtime.

In pratica, si utilizza First / FirstOrDefault spesso anche nei casi in cui si presuppone un singolo elemento, per migliorare le prestazioni. Dovresti comunque ricordare che Single / SingleOrDefault può migliorare la leggibilità (perché indica l'assunzione di un singolo elemento) e la stabilità (perché lo controlla) e usarlo in modo appropriato.


Entrambi sono gli operatori dell'elemento e sono usati per selezionare un singolo elemento da una sequenza. Ma c'è una piccola differenza tra loro. L'operatore SingleOrDefault () genererebbe un'eccezione se più di un elemento è soddisfatto della condizione in cui FirstOrDefault () non genererà alcuna eccezione per lo stesso. Ecco l'esempio.

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();

Nei tuoi casi, vorrei usare il seguente:

seleziona per ID == 5: è OK usare SingleOrDefault qui, perché ti aspetti un'entità [o nessuna], se hai più di un'entità con ID 5, c'è qualcosa di sbagliato e sicuramente un'eccezione degna.

quando si cercano persone il cui nome è uguale a "Bobby", ce ne possono essere più di uno (molto probabilmente ci penserei), quindi non si dovrebbe usare né Single né First, basta selezionare con l'operazione Where (se "Bobby" restituisce troppe entità, l'utente deve affinare la sua ricerca o scegliere uno dei risultati restituiti)

l'ordine per data di creazione dovrebbe anche essere eseguito con un'operazione Where (difficilmente avrà solo un'entità, l'ordinamento non sarebbe di grande utilità;) questo tuttavia implica che TUTTE le entità siano ordinate - se si desidera UNO, utilizzare FirstOrDefault, La singola getterebbe ogni volta se hai più di un'entità.


Nel tuo ultimo esempio:

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

Sì, lo fa. Se si tenta di utilizzare SingleOrDefault() e la query risulta in più del record si otterrebbe ed eccezione. L'unica volta che puoi tranquillamente usare SingleOrDefault() è quando ti aspetti solo 1 e solo 1 risultato ...


Non capisco perché stai usando FirstOrDefault(x=> x.ID == key) quando questo potrebbe recuperare i risultati molto più velocemente se usi Find(key) . Se stai interrogando con la chiave primaria della tabella, la regola empirica è di usare sempre Find(key) . FirstOrDefault dovrebbe essere usato per roba di predicato come (x=> x.Username == username) ecc.

questo non meritava un downvote in quanto l'intestazione della domanda non era specifica per linq su DB o Linq su List / IEnumerable ecc.


Ogni volta che utilizzi SingleOrDefault , SingleOrDefault chiaramente che la query dovrebbe comportare al massimo un singolo risultato. D'altra parte, quando viene utilizzato FirstOrDefault , la query può restituire qualsiasi quantità di risultati, ma si afferma che si desidera solo il primo.

Personalmente trovo la semantica molto diversa e uso quella appropriata, a seconda dei risultati attesi, migliora la leggibilità.


Se il tuo set di risultati restituisce 0 record:

  • SingleOrDefault restituisce il valore predefinito per il tipo (ad es. Il valore predefinito per int è 0)
  • FirstOrDefault restituisce il valore predefinito per il tipo

Se si imposta set restituisce 1 record:

  • SingleOrDefault restituisce quel record
  • FirstOrDefault restituisce quel record

Se il tuo set di risultati restituisce molti record:

  • SingleOrDefault genera un'eccezione
  • FirstOrDefault restituisce il primo record

Conclusione:

Se si desidera generare un'eccezione se il set di risultati contiene molti record, utilizzare SingleOrDefault .

Se si desidera sempre 1 record indipendentemente dal contenuto del set di risultati, utilizzare FirstOrDefault


SingleOrDefault: stai dicendo che "Al massimo" c'è un elemento che corrisponde alla query o predefinito FirstOrDefault: stai dicendo che c'è "Almeno" un elemento corrispondente alla query o predefinito

Dillo ad alta voce la prossima volta che devi scegliere e probabilmente sceglierai saggiamente. :)


Per LINQ -> SQL:

SingleOrDefault

  • genererà query come "select * from users where userid = 1"
  • Seleziona record corrispondente, genera un'eccezione se vengono trovati più record
  • Utilizzare se si stanno recuperando dati in base alla colonna chiave primaria / univoca

FirstOrDefault

  • genererà query come "seleziona top 1 * dagli utenti dove userid = 1"
  • Seleziona le prime righe corrispondenti
  • Utilizzare se si stanno recuperando dati basati su una colonna chiave non primaria / univoca






linq-to-sql