Sum() вызывает исключение вместо возврата 0, когда нет строк
У меня есть этот код (хорошо, я не знаю, но что-то похожее: p)
var dogs = Dogs.Select(ø => new Row
{
Name = ø.Name,
WeightOfNiceCats = ø.Owner
.Cats
.Where(æ => !æ.Annoying)
.Sum(æ => æ.Weight),
});
Здесь я просматриваю всех собак и подытоживаю вес (в нечетную десятичную цифру) всех не раздражающих кошек, которые имеют того же владельца, что и собака. Конечно, почти все кошки раздражают, поэтому я получаю эту ошибку:
Нулевое значение не может быть присвоено члену с типом System.Decimal, который является типом значения, не имеющим значения NULL.
Ни одно из используемых полей или внешних ключей не может быть нулевым. Таким образом, ошибка возникает, когда предложение Where
не возвращает кошек, что часто делает. Но как я могу это решить? Я хочу, чтобы он вернулся 0, когда это произойдет. Пробовал с помощью DefaultIfEmpty()
после предложения Where
, но затем я получаю эту ошибку:
Ссылка на объект не установлена в экземпляр объекта.
Я думаю, это понятно. Я попытался добавить ??
после Sum
, но тогда он не скомпилируется из-за этой ошибки:
Оператор '??' не может применяться к операндам типа "десятичный" и "десятичный"
Что тоже имеет смысл, конечно. Так что я могу сделать? Было бы неплохо, если бы вещь Sum
только что вернулась 0, когда нечего было суммировать. Или оператор SumOrZero
. Было бы трудно сделать метод SumOrZero
, который работал с Linq2SQL?
Ответы
Ответ 1
Это то, что я закончил на данный момент:
.Sum(æ => (decimal?) æ.Weight) ?? 0.0M,
Это работает как шарм.
Я все еще предпочел бы иметь SumOrZero
я мог бы использовать, который работал бы точно так же, как обычная Sum
за исключением того, что он никогда не вернул бы null
по любой причине. Как уже отмечали другие, это было бы довольно легко сделать для IEnumerable
но немного более IQueryable
для IQueryable
поэтому он будет работать с Linq2SQL и т.д. Поэтому я просто пока оставлю это на этом. Хотя если кому-то скучно однажды и написать SumOrZero
для IQueryable
который работает с Linq2SQL для всех числовых типов, пожалуйста, дайте мне знать: D
Ответ 2
В LINQ to Objects это будет легко. С LINQ to SQL это будет сложнее. Вы можете написать свой собственный метод расширения, который называется Queryable.Sum
с выражением, созданным из обычного, с приведением к типу с нулевым значением. Я подозреваю, что вам нужно будет сделать? 0m в вызывающем коде. Другими словами, вы могли бы сделать:
.SumOrNull(æ => æ.Weight) ?? 0M,
Где подпись для .SumOrNull будет:
public static decimal? SumOrNull<TSource, decimal>(this IQueryable<TSource>,
Func<TSource,decimal> projection)
В основном вы можете написать это для всех типов значений, поддерживаемых в Queryable. Вы можете написать его в общем виде и вызвать соответствующий метод в Queryable с помощью отражения, но это тоже было бы неприятно.
Я думаю, что вам лучше всего быть с тем, что у вас есть, если честно.
Ответ 3
Трюк, который вы уже указали (.Sum(æ => (decimal?) æ.Weight) ?? 0.0M
), - это лучшее, что я могу придумать для этого сценария, когда он выполняется как запрос в базе данных. Немного раздражает, но есть еще хуже...
В отличие от LINQ-to-Objects, вы не можете просто добавить свой собственный метод расширения, поскольку он должен отображаться базовым провайдером.
Ответ 4
Вы хотите использовать DefaultIfEmpty, который будет возвращать IEnumberable с одним элементом из 0, и это будет семантически то же, что вам нужно.
Поскольку у вас есть десятичное число с нулевым значением, вам, вероятно, придется использовать 2-ю версию метода с двумя параметрами.