2016-02-11 15 views
5

Ecco l'esempio di codice:Perché l'eccezione non è stata rilevata nella chiusura?

public IList<LogEntry> ReadLogs(Guid id, string name) 
    { 
     var logs = this.RetrieveLogs(id, name); 

     if (logs != null) 
     { 
      foreach (LogEvent logEvent in logs) 
      { 
       // bla, bla, bla 
      } 
     } 

     return logEntries; 
    } 

    private IEnumerable<LogEvent> RetrieveLogs(Guid id, string name) 
    { 
     try 
     { 
      FilterCriteria filterCriteria = CreateFilterCriteria(); 
      return (from log in this.loggingProvider.ReadLogs(filterCriteria, 1) 
        where log.ParticipantObjects[0].ParticipantObjectId == id.ToString() 
        && log.LogEventParameters[0].Value == name 
        orderby log.Timestamp.ToLocalTime() descending 
        select log).AsEnumerable(); 
     } 
     catch (Exception ex) 
     { 
      this.tracer.Write(Category.Error, ex, "Error"); 
      return null; 
     } 
    } 

Ora, se ci fosse un'eccezione all'interno di loggingProvider.ReadLogs() metodo, saranno catturati e rintracciati. Ma se, ad esempio, non esiste lo ParticipantObjects[0], questa eccezione non verrà rilevata e tracciata qui. Sembra che abbia qualcosa a che fare con espressioni e chiusure lambda.

Qual è la spiegazione?

+1

una query non viene eseguita finché non si fa qualcosa con esso – user1666620

risposta

5

loggingProvider.ReadLogs viene eseguito all'interno di RetrieveLogs. Ma tutti gli altri lambda vengono eseguiti solo quando si esegue l'iterazione su di essi nell'esterno this.ReadLogs.

Quindi l'eccezione viene generata nel metodo superiore e pertanto non può essere rilevata all'interno di RetrieveLogs.

Per evitare ciò, modificare AsEnumerable a ToList() o ToArray() per assicurarsi che la query venga effettivamente eseguita prima di tornare.

3

Un risultato IEnumerable da una query LINQ viene valutato solo quando viene enumerato.

Affinché il Exception venga generato dove previsto, è necessario enumerare il risultato della query LINQ IEnumerable utilizzando uno dei metodi di estensione pertinenti come ToList().

Come un lato nota (prestito da Rene Vogt) si potrebbe evitare il Exception che sono gettati scrivendo il tuo LINQ come segue:

return this.loggingProvider.ReadLogs(filterCriteria, 1).Where(log => 
    log.ParticipantObjects.FirstOrDefault() != null && 
    log.ParticipantObjects[0].ParticipantObjectId == id.ToString() && 
    log.LogEventParameters[0].Value == name).OrderBy(log => 
    log.Timestamp.ToLocalTime()).ToList(); 
+0

No, non è quello che intendevo. L'ho appena scritto in questo modo perché mi consente di leggere più facilmente del linguaggio di query. Penso che il risultato sia lo stesso finché non si cambia 'AsEnumerable()' in qualcosa che esegue _executes_ la query come 'ToList()' o 'ToArray()'. –

+0

È davvero più facile. Il mio punto è che la condizione extra non genererebbe mai 'NullReferenceException' o' IndexOutOfRangeException' a causa del controllo che 'ParticipantObjects [0]' esista. – toadflakz

+0

Quindi potresti voler cambiare 'log.ParticipantObjects.First()! = Null' a' log.ParticipantObjects.FirstOrDefault()! = Null', perché 'Primo' getta se la selezione è vuota;) –

15

ho detto prima e lo sarà senza dubbio lo ripeto: la cosa più importante da sapere su una query è che una domanda è una domanda, non la risposta alla domanda.

L'oggetto query che hai creato non viene eseguito semplicemente costruendolo. Stai costruendo la domanda. La domanda in realtà non ottiene e ha risposto finché non si esegue la query. E l'esecuzione è al di fuori del blocco try.

+0

Dato che hai già dato per scontato quello che è successo, lasciatemi solo commentare che non ho creato o eseguito la query, stavo semplicemente analizzando il codice di qualcun altro (qui è molto semplificato) che ha prodotto un bug.Anche se sapevo già che cosa hai dichiarato qui, semplicemente non ho notato che era AsEnumerable alla fine della dichiarazione, invece di ToList e, per qualche ragione, ho pensato che fosse una qualche strana ottimizzazione riguardante chiusure. –

+0

@NenadDobrilovic: Bene, allora sostituisci mentalmente "qualcun altro" per "tu" nel secondo paragrafo. Sono contento che tu abbia capito il tuo errore - non notando che la query non è stata eseguita - e quindi sono meno propensi a farlo in futuro, e sono felice che tu abbia capito dove si trova l'errore logico nel codice, se in effetti ce n'è uno. –

Problemi correlati