Sum () вызывает исключение вместо возврата 0, когда нет строк - PullRequest
18 голосов
/ 23 февраля 2009

У меня есть этот код (хорошо, нет, но что-то похожее: p)

    var dogs = Dogs.Select(ø => new Row
    {
            Name = ø.Name,
            WeightOfNiceCats = ø.Owner
              .Cats
              .Where(æ => !æ.Annoying)
              .Sum(æ => æ.Weight),
    });

Здесь я просматриваю всех собак и суммирую вес (в ненулевой десятичной дроби) всех не раздражающих кошек, у которых тот же владелец, что и у собаки. Конечно, почти все кошки раздражают, поэтому я получаю эту ошибку:

Нулевое значение не может быть назначено члену с типом System.Decimal, который является необнуляемым типом значения.

Ни одно из используемых полей или внешних ключей не может быть нулевым. Таким образом, ошибка возникает, когда предложение Where не возвращает кошек, что часто происходит. Но как я могу решить это? Я хочу вернуть 0, когда это произойдет. Пробовал с DefaultIfEmpty() после предложения Where, но затем я получаю эту ошибку:

Ссылка на объект не установлена ​​для экземпляра объекта.

Что, я думаю, понятно. Я попытался добавить ?? после Sum, но потом он не скомпилируется из-за этой ошибки:

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

Что также имеет смысл, конечно. Так что я могу сделать? Было бы хорошо, если бы вещь Sum просто вернула 0, когда нечего было суммировать. Или SumOrZero заявление некоторого вида. Было бы сложно создать метод SumOrZero, который работал бы с Linq2SQL?

Ответы [ 4 ]

21 голосов
/ 23 февраля 2009

Это то, что я закончил на данный момент:

.Sum(æ => (decimal?) æ.Weight) ?? 0.0M,

Это работает как шарм.

Я бы все же предпочел иметь SumOrZero, который мог бы использовать, который работал бы точно так же, как обычный Sum, за исключением того, что он никогда не вернул бы null по любой причине. Как уже отмечали другие, это было бы довольно просто сделать для IEnumerable, но немного более неприлично для IQueryable, так что это будет работать с Linq2SQL и т. Д. Так что я пока оставлю это на этом. Хотя если кому-то скучно однажды и написать SumOrZero для IQueryable, который работает с Linq2SQL для всех числовых типов, пожалуйста, дайте мне знать: D

5 голосов
/ 23 февраля 2009

В LINQ to Objects это было бы легко. С LINQ to SQL будет сложнее. Вы можете написать свой собственный метод расширения, который называется Queryable.Sum с выражением, построенным из обычного, с приведением к типу NULL. Я подозреваю, что вам нужно сделать ?? 0m в коде вызова, хотя. Другими словами, вы можете сделать:

.SumOrNull(æ => æ.Weight) ?? 0M,

Где будет подпись .SumOrNull:

public static decimal? SumOrNull<TSource, decimal>(this IQueryable<TSource>,
    Func<TSource,decimal> projection)

Вы можете написать это для всех типов значений, поддерживаемых в Queryable. Вы могли бы написать его в общем и вызвать соответствующий метод в Queryable, используя отражение, но это тоже было бы странно.

Я думаю, вам лучше всего иметь то, что у вас есть, если честно.

3 голосов
/ 23 февраля 2009

Уловка, которую вы уже дали (.Sum(æ => (decimal?) æ.Weight) ?? 0.0M), - лучшее, что я могу придумать для этого сценария, когда он выполняется как запрос к базе данных. Немного раздражает, но есть и худшие вещи ...

В отличие от LINQ-to-Objects, вы не можете просто добавить свой собственный метод расширения, так как он должен отображаться соответствующим провайдером.

0 голосов
/ 23 февраля 2009

Вы хотите использовать DefaultIfEmpty, который будет возвращать IEnumberable с одним элементом 0 и будет семантически таким же, как вам требуется.

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

...