2009-08-17 4 views
12

Sono nuovo a LINQ e sto provando a creare alcuni punti dati da una tabella a un grafico. I tre campi di importanza in questa tabella sono l'id, il tempo e il valore. Sto scrivendo una query per ottenere il valore medio su un tempo prestabilito per un ID scelto. LINQ ho scritto segue:Gestione risultati nulli con il metodo LINQ Average()

var value = (from t in _table 
      where t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime 
      select t.Value).Average(); 

Tuttavia questo si blocca in fase di esecuzione con:

"il valore null non può essere assegnato a un membro di tipo System.Decimal che è un non-nullable valore tipo .. "

A determinati intervalli non ci sono dati quindi SQL LINQ genera resi nulli, che mi piacerebbe essere COALESCED a 0 ma invece blocca l'applicazione. C'è un modo per scrivere questa query LINQ per essere in grado di gestirlo correttamente?

La definizione della tabella per rendere le cose più chiare:

[Serializable] 
[Table(Name = "ExampleTable")] 
public class ExampleTable 
{ 
    [Column(Name = "Id")] 
    public int Id { get; set; } 

    [Column(Name = "Time")] 
    public DateTime Time { get; set; } 

    [Column(Name = "Value")] 
    public int Value{ get; set; } 
} 

risposta

19

Penso che si desidera

var value = (from t in _table 
      where t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime 
      select (int?)t.Value).Average() 

In questo modo, si ottiene un double? schiena, mentre senza il (int?) getto è necessario per ottenere un double indietro, che non può essere null.

Questo perché delle firme

double Enumerable.Average(IEnumerable<int> source) 
double? Enumerable.Average(IEnumerable<int?> source) 

Ora, per ottenere una media di 0, invece di null, è necessario posizionare l'operatore coalescenza alla fine

var value = (from t in _table 
      where t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime 
      select (int?)t.Value).Average() ?? 0.0; 

IMHO questo è un design piuttosto orribile della classe Enumerable/Queryable; perché non è possibile restituire Average(IEnumerable<int>), perché solo per Average(IEnumerable<int?>)?

+0

Grazie, hai fatto. – Magpie

+0

solo per aiutare gli altri in questo problema: se la query * non restituisce alcuna riga *, nulla sarà di grande aiuto. devi solo evitare di eseguire la query del tutto. almeno questa è stata la mia esperienza. – horace

+0

@horace: Semplicemente non è vero. Media, Min, Max, Sum funzionano tutte quando la query non corrisponde a righe * a condizione che il risultato aggregato sia annullabile *. Come puoi vedere nella query sopra, avrai bisogno di un cast per rafforzare il nullable quando l'espressione da aggregare non è di tipo nullable. (Nel caso raro che non funzioni ancora, probabilmente stai trattando con un provider LINQ sfacciato. Non è colpa di LINQ, e nessun fornitore che conosco è incasinato.) – Ruben

15

EDIT: cambio completo :)

Okay, come su questo:

var value = (from t in _table 
      where t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime 
      select t.Value).DefaultIfEmpty().Average() 

Credo che sia logicamente quello che voglio - cambiando {} a {0}, in modo da rendere tutte le medie ottenibili. Non so se farà quello che vuoi in termini di SQL però.

+0

Spiacente, questo non funziona come valore è un int non nullable. – Magpie

+0

Quindi rimuovere la "m" dallo 0 e dovrebbe funzionare bene. –

+0

Ah, mi sto avvicinando in modo errato ... aspetta. –

1

EDIT: Totale rilavorazione

Prova a trasmettere il valore nullable prima

var value = (from t in _table 
     where t.Id == id 
      && t.Time >= intervalStartTime 
      && t.Time <= intervalEndTime 
     select ((int?)t.Value) ?? 0).Average() 
+0

seleziona t.Value ?? 0 non viene compilato in quanto t.Value è un int non nullable. – Magpie

+0

hai provato questa modifica? qual'è il risultato? –

+0

Sì, il compilatore mi dice che l'operando di sinistra non sarà mai nullo in quanto il valore non è annullabile – Magpie

0

provare quanto segue. Salterà semplicemente tutti gli oggetti nulli restituiti dalla query.

var value = (from t in _table 
      where t != null 
      where t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime 
      select t.Value).Average(); 

Se si desidera trattare in modo esplicito gli elementi nulli come zero, quindi un semplice uso del operatore condizionale dovrebbe fare il lavoro:

var value = (from t in _table 
      where t == null || 
       (t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime) 
      select t == null ? 0 : t.Value).Average(); 
+0

Come posso 't' essere nullo, e essere tra' intervalStartTime' e 'intervalEndTime'? Penso che il suo problema sia che non ci sono record tra certe ore di inizio e di fine, non che ci siano valori nulli nella tabella. – Blorgbeard

+0

Oh, t.Value vs. t.Time - Vedo. – Blorgbeard

+0

Grazie per il suggerimento, ma ancora lancia la stessa eccezione. – Magpie

0

Hai bisogno di un temporaneo per la query iniziale ?

es:

var temp = (from t in _table 
      where t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime 
      select t.Value) ?? new List<int>() {0}; 
var value = temp.Average(); 

Non sono sicuro se questo aiuta.

Problemi correlati