简体   繁体   中英

EF Core Average with no sequence

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM