Подтвердить что ты не робот

Sum() Возвращает null в запросе Entity Framework

У меня есть большой запрос Entity Framework, содержащий эти строки.

var programs = from p in Repository.Query<Program>()
               where p.OfficeId == CurrentOffice.Id
               let totalCharges = p.ProgramBillings.Where(b => b.Amount > 0 && b.DeletedDate == null).Select(b => b.Amount).Sum()
               let totalCredits = p.ProgramBillings.Where(b => b.Amount < 0 && b.DeletedDate == null).Select(b => -b.Amount).Sum()
               let billingBalance = (totalCharges - totalCredits)

Когда я материализую данные, я получаю следующую ошибку:

Приведение значения к типу значения "Десятичный" завершилось неудачно, поскольку материализованное значение равно нулю. Либо общий параметр типа результата, либо запрос должен использовать тип с нулевым значением.

Если я изменил свой запрос следующим образом (добавленный в двух типах), ошибка исчезнет.

var programs = from p in Repository.Query<Program>()
               where p.OfficeId == CurrentOffice.Id
               let totalCharges = (decimal?)p.ProgramBillings.Where(b => b.Amount > 0 && b.DeletedDate == null).Select(b => b.Amount).Sum()
               let totalCredits = (decimal?)p.ProgramBillings.Where(b => b.Amount < 0 && b.DeletedDate == null).Select(b => -b.Amount).Sum()
               let billingBalance = (totalCharges - totalCredits)

Я не понимаю этого. ProgramBilling.Amount является непустым значением десятичного числа. Если я нахожусь над вызовом Sum(), Intellisense говорит, что возвращает тип Decimal. И все же дополнительные тесты подтвердили, что в моей второй версии totalCharges и totalCredits оба установлены равными нулю для тех строк, где ProgramBillings не имеет данных.

Вопросы:

  • Я понял, что Sum() возвращает 0 для пустой коллекции. При каких обстоятельствах это не так?

  • И если иногда это не так, то почему, когда я наводил курсор на Sum(), Intellisense показывает, что он возвращает тип Decimal, а не Decimal? Похоже, у Intellisense было такое же понимание, что и у меня.

EDIT:

Казалось бы, легко сделать что-то вроде Sum() ?? 0m. Но это незаконно, давая мне ошибку:

Оператор '??' не может применяться к операндам типа "десятичный" и "десятичный"

4b9b3361

Ответ 1

Я понял, что Sum() возвратил 0 для пустой коллекции. При каких обстоятельствах это не так?

Если вы не используете LINQ для объектов, как это имеет место здесь. Здесь у вас есть поставщик запросов, который переводит этот запрос в SQL. Операция SQL имеет другую семантику для оператора SUM.

И если иногда это не так, то почему, когда я наводил курсор на Sum(), Intellisense показывает, что он возвращает тип Decimal, а не Decimal? Похоже, у Intellisense было такое же понимание, что и у меня.

Оператор С# LINQ SUM не возвращает значение NULL; он должен иметь ненулевое значение, но оператор SQL SUM имеет другую семантику, он возвращает null при суммировании пустого набора, а не 0. Тот факт, что значение null предоставляется в контексте, где С# требует непустого значения, является всей причиной, по которой все ломается. Если оператор С# LINQ SUM здесь возвращал значение с нулевым значением, то null можно было бы просто вернуть без каких-либо проблем.

Это различия между оператором С# и оператором SQL, который используется для представления того, что вызывает эту ошибку.

Ответ 2

У меня есть одна и та же проблема в одном из моих запросов EF, когда коллекция пуста, одно быстрое исправление для этого заключается в том, чтобы отбрасывать до нулевой десятичной:

var total = db.PaiementSet.Sum(o => (Decimal?)o.amount) ?? 0M;

надеюсь, что это поможет.

Ответ 3

До .Sum добавьте DefaultIfEmpty (0.0M)