Ho un problema quando da List voglio raggruppare per proprietà e prendere la media o la somma di proprietà specifiche che sto ricevendo errore La sequenza non contiene elementi. Di quanto ho messo DefaultIfEmpty, che ottengo diverso errore NullReferenceException: riferimento oggetto non impostato su un'istanza di un oggetto.
Il codice è come questo:
var items = _repository.GetAllItems();
var groupedItems = items.GroupBy(x=> new {Year = x.DateCreate.Year, Month = x.DateCreate.Month})
.Select(s=> new ItemForListViewModel(){
Year = s.Key.Year,
Month = s.Key.Month,
AvgQnt = s.Where(x=>x.Price > 10).Average(x=>x.Qnt)
}).ToList();
Il codice dall'alto dà errore La sequenza non contiene elementi, di quanto non cambi io
var groupedItems = items.GroupBy(x=> new {Year = x.DateCreate.Year, Month = x.DateCreate.Month})
.Select(s=> new ItemForListViewModel(){
Year = s.Key.Year,
Month = s.Key.Month,
AvgQntPrice10 = s.Where(x=>x.Price > 10).DefaultIfEmpty().Average(x=>x.Qnt),
AvgQntPrice100 = s.Where(x=>x.Price > 100).DefaultIfEmpty().Average(x=>x.Qnt
}).ToList();
Poi ricevo un nuovo errore: NullReferenceException: il riferimento all'oggetto non è impostato su un'istanza di un oggetto.
Nel database se eseguo la query ottengo 0 per AvgQntPrice10 e l'esempio 15 per AvgQntPrice100 che cosa è corretto.
Saluti, Danijel
Il problema è ovviamente che dopo DefaultIfEmpty
il parametro x
delle chiamate Average
può essere null
(il valore predefinito CLR per i tipi di riferimento).
Torna al problema originale: la sequenza non contiene eccezioni agli elementi quando si chiama Min
, Max
o Average
sulla raccolta vuota. Può essere risolto correttamente in due modi.
Il primo è, invece di DefaultIfEmpty().Average(selector)
, usa la combinazione non così concisa, ma operativa Select(selector).DefaultIfEmpty().Average()
, ad esempio
AvgQntPrice10 = s.Where(x => x.Price > 10).Select(x => x.Qnt).DefaultIfEmpty().Average(),
AvgQntPrice100 = s.Where(x => x.Price > 100).Select(x => x.Qnt).DefaultIfEmpty().Average()
Il secondo (e il mio preferito) è quello di utilizzare il fatto che gli overload nullable dei metodi Min
, Max
e Average
non generano Sequence non contiene elementi di eccezione per la raccolta vuota, ma restituiscono invece null
. Quindi tutto ciò di cui hai bisogno è di lanciare il tipo di espressione del selettore sul tipo nullable corrispondente, e opzionalmente usare il ??
sul risultato del metodo aggregato per assegnare un valore speciale per quel caso (come 0
).
Ad esempio, se il tipo di Qnt
è int
(in caso contrario, usa il tipo corretto), quanto sopra potrebbe essere scritto come
AvgQntPrice10 = s.Where(x => x.Price > 10).Average(x => (int?)x.Qnt) ?? 0,
AvgQntPrice100 = s.Where(x => x.Price > 100).Average(x => (int?)x.Qnt) ?? 0