EF Core Average with no sequence

.net-core c# entity-framework-core

Question

I have problem when from List want to do grouping by properties and take average or sum of specific properties I'm getting error Sequence does not contain elements. Than I put DefaultIfEmpty, than I get different error NullReferenceException: Object reference not set to an instance of an object.

Code is like this:

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

Code from above gives error Sequence does not contain elements, than I change

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

Than I get new error: NullReferenceException: Object reference not set to an instance of an object.

In database if I run query I get 0 for AvgQntPrice10 and example 15 for AvgQntPrice100 what is correct.

Regards, Danijel

1
2
1/29/2019 5:54:18 PM

Accepted Answer

The problem is of course that after DefaultIfEmpty the parameter x of Average calls can be null (the CLR default value for reference types).

Back to the original problem - Sequence does not contain elements exception when calling Min, Max or Average on empty collection. It can be solved correctly in two ways.

First is, instead of DefaultIfEmpty().Average(selector), use the not so concise, but working combination Select(selector).DefaultIfEmpty().Average(), e.g.

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

Second (and my preferred) is to utilize the fact the nullable overloads of the Min, Max and Average methods do not throw Sequence does not contain elements exception for empty collection, but return null instead. So all you need is to cast the selector expression type to the corresponding nullable type, and optionally use the ?? on the aggregate method result to assign a special value for that case (like 0).

For instance, if the type of the Qnt is int (if not, use the proper type), the above could be written as

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
3
1/29/2019 5:54:07 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow