Oggetto nullo o vuoto quando la query LINQ su Entità non restituisce nulla

2

Dire che ho una query LINQ come questa:

application = CreditDatabase
                        .Applications
                        .Select(Mapper.Map<Application>) 
                        .Where(c => c.uID == urID)
                        .DefaultIfEmpty().First();

Restituisce null se la query LINQ restituisce un set di risultati vuoto. È corretto". È meglio restituire un oggetto vuoto? Se è così allora come posso farlo? Questo link dice che non è possibile restituire un tipo di riferimento non nullo: link

    
posta w0051977 08.07.2017 - 14:12
fonte

3 risposte

5

Le query del database restituiscono risultato set . Un set vuoto è una risposta ragionevole; significa che non hai nessuna delle cose cercate.

Direi che il tuo codice sta andando molto in canonicalizzazione del risultato.

Il tuo codice (da qualche parte poco dopo questa riga) sta andando a verificare se l'applicazione che hai appena cercato esiste o no, quindi non stai impedendo la successiva logica condizionale (non mostrata nella tua domanda) nel tuo codice facendo %codice%.

Puoi - e io sostengo dovrebbe - invece: controlla il set vuoto (ad esempio, utilizza la dimensione del set di risultati, magari con .FirstOrDefault() ) per vedere se l'applicazione esiste. Questo è sostanzialmente più pulito rispetto alla conversione del set vuoto su default e poi prendendo il primo e infine verificando che null per vedere se l'applicazione di interesse esiste.

Piuttosto che andare così lontano:

application = CreditDatabase
                    .Applications
                    .Select(Mapper.Map<Application>) 
                    .Where(c => c.uID == urID)
                    .DefaultIfEmpty().First();
if ( application == null ) {
   // handle missing application, maybe add it...
} else {
    // use the found application
}

Ometti la gestione predefinita; invece:

search = CreditDatabase
                    .Applications
                    .Select(Mapper.Map<Application>) 
                    .Where(c => c.uID == urID);
if ( ! search.Any () ) {
    // handle missing application, maybe add it...
} else {
    application = search.Single ();
    // use the found application
}

Addendum: in .Any() sopra, ho usato application = search.Single () invece di .Single() poiché eccetto se il set di risultati non vuoto ha più di una corrispondenza ha senso qui: è una ricerca di cosa presumiamo che esista una corrispondenza esatta con la chiave primaria univoca (altrimenti, come sottolinea @JacquesB, poiché la query non è ordinata, non sappiamo davvero cosa stiamo mantenendo e cosa stiamo eliminando con .First() ).

    
risposta data 08.07.2017 - 17:15
fonte
2

Dipende!

Ma prima un chiarimento: .DefaultIfEmpty().First() può essere scritto più semplice come .FirstOrDefault() che fa lo stesso - restituisce il primo elemento, o null se il risultato è vuoto. Ma probabilmente hai un bug qui: First() indica che potresti avere più oggetti e vuoi selezionare il primo oggetto - ma poiché non vi è alcun ordine implicato significa che otterrai casuale articolo. Questo probabilmente non è quello che vuoi! Se ti aspetti davvero che ci possano essere più corrispondenze, restituisci semplicemente l'elenco delle partite. Se prevedi un singolo articolo, utilizza invece Single() e se ti aspetti un singolo elemento o nulla, utilizza SingleOrDefult() che restituirà anche null se non ci fosse corrispondenza.

Ora alla domanda interessante: cosa restituire da un metodo che trova o un singolo oggetto o niente?

Ci sono diverse opzioni a seconda di cosa vuoi ottenere:

  • Restituisce null se l'elemento non viene trovato. Vantaggi: questo è molto semplice. Svantaggi: il client deve controllare null che è facilmente dimenticato perché il sistema di tipi non applica questo controllo. Ciò potrebbe portare a un aumento del coefficiente di sicurezza di% su% della linea. Non consigliato.

  • Genera un'eccezione se l'oggetto non viene trovato. Vantaggi: semplice e sicuro. Svantaggi: dovrebbe essere usato solo se indica un bug se l'oggetto non esiste.

  • Crea un metodo separato che ti permetta di verificare se l'oggetto esiste (ad esempio NullReferenceExceptions ). Questo metodo restituisce un valore booleano. Vantaggi: codice chiaro sul lato client. Svantaggi: colpirà il database due volte, a meno che non si utilizzi una sorta di caching, quindi potrebbe essere costoso. Anche più codice.

  • Utilizza il modello if (ApplicationExists(id))... . Restituisce un valore booleano che indica se l'oggetto esiste e posiziona l'oggetto (se presente) in un parametro TryXXX . Vantaggi: questo pattern è già noto da say out , quindi i lettori non saranno confusi. Svantaggio: il parametro out porta a un codice un po 'brutto, sebbene questo sia migliorato in C # 6.

  • Restituisce un tipo Dictionary.TryGetValue() . Ciò richiede al client di controllare e scartare esplicitamente l'oggetto. Vantaggi: molto più sicuro e più elegante rispetto all'utilizzo di null. Svantaggio: C # non ha un tipo Option<T> incorporato, devi trovarne uno o scriverne uno da solo.

L'unica cosa che non dovresti fare:

  • restituisce un'istanza "vuota" della classe.
risposta data 08.07.2017 - 18:36
fonte
0

Mi piace risintonizzare null quando qualcosa non viene trovato. Per me ha senso.

Dammi il primo elemento che corrisponde a questo criterio .. non ce ne sono, qui è un null. Il tuo codice genererà un'eccezione se tenta di usarlo.

Un'alternativa se non ti piace testare null o oggetti vuoti, anche se è sempre quello di restituire un elenco di oggetti.

In questo modo puoi fare uno per ciascuno nella lista e naturalmente non fa nulla se non viene trovato nulla.

Non è possibile sovrascrivere il comportamento predefinito, ma è possibile utilizzare la concatenazione nulla

var x = Applications.FirstOrDefault() ?? new Application();

Ps. sposta la mappatura dopo la clausola where!

    
risposta data 08.07.2017 - 15:09
fonte

Leggi altre domande sui tag