Как заставить LINQ Sum() вернуть 0, когда исходный набор пуст

В основном, когда я выполняю следующий запрос, если ни один из них не был сопоставлен, следующий запрос выдает исключение. В этом случае я предпочел бы, чтобы сумма выравнивала 0, а не вызывало исключение. Возможно ли это в самом запросе - я имею в виду вместо сохранения запроса и проверки query.Any()?

double earnings = db.Leads.Where(l => l.Date.Day == date.Day
                && l.Date.Month == date.Month
                && l.Date.Year == date.Year
                && l.Property.Type == ProtectedPropertyType.Password
                && l.Property.PropertyId == PropertyId).Sum(l => l.Amount);

Ответ 1

Попробуйте изменить свой запрос на это:

db.Leads.Where(l => l.Date.Day == date.Day
            && l.Date.Month == date.Month
            && l.Date.Year == date.Year
            && l.Property.Type == ProtectedPropertyType.Password
            && l.Property.PropertyId == PropertyId)
         .Select(l => l.Amount)
         .DefaultIfEmpty(0)
         .Sum();

Таким образом, ваш запрос будет выбирать только поле Amount. Если коллекция пуста, она вернет один элемент со значением 0, а затем будет применена сумма.

Ответ 2

Я предпочитаю использовать другой хак:

double earnings = db.Leads.Where(l => l.Date.Day == date.Day
                                      && l.Date.Month == date.Month
                                      && l.Date.Year == date.Year
                                      && l.Property.Type == ProtectedPropertyType.Password
                                      && l.Property.PropertyId == PropertyId)
                          .Sum(l => (double?) l.Amount) ?? 0;

Ответ 3

Попробуйте это вместо этого: короче:

db.Leads.Where(..).Aggregate(0, (i, lead) => i + lead.Amount);

Ответ 4

Это победа для меня:

int Total = 0;
Total = (int)Db.Logins.Where(L => L.id == item.MyId).Sum(L => (int?)L.NumberOfLogins ?? 0);

В моей таблице LOGIN в поле NUMBEROFLOGINS некоторые значения имеют значение NULL, а другие имеют номер INT. Здесь я суммирую NUMBEROFLOGINS всех пользователей одной корпорации (каждый идентификатор).

Ответ 5

Обновление: Хотя это и рабочее решение, этот ответ неверен. Правильный - тот, который дал Саймон Белэнджер. Тем не менее, вместо удаления я оставляю это здесь в качестве дополнительной информации (см. комментарии).

Интересная деталь здесь в том, что реализация LINQ To SQL в Sum (которую вы используете) отличается от реализации LINQ To Objects. Ответ саймона Беланджера правильный, и мне нравится это, но (не для поиска далеко) другой вариант, чтобы заставить эту работу работать, это изменить тип компиляции источника и использование LINQ To Objects. Будет выполнен другой Sum (без каких-либо дополнительных операций):

db.Leads.Where(...)
        .AsEnumerable() // switch to LINQ to Objects
        .Sum(l => l.Amount);

Из MSDN:

Метод AsEnumerable (IEnumerable) не имеет никакого эффекта кроме того, чтобы изменить тип источника времени компиляции от типа, который реализует IEnumerable сам IEnumerable.

Ответ 6

db.Leads.Where(l => l.Date.Day == date.Day
        && l.Date.Month == date.Month
        && l.Date.Year == date.Year
        && l.Property.Type == ProtectedPropertyType.Password
        && l.Property.PropertyId == PropertyId)
     .Select(l => l.Amount)
     .ToList()
     .Sum();

Ответ 7

Попробуйте:

double earnings = db.Leads.Where(l => l.ShouldBeIncluded).Sum(l => (double?) l.Amount) ?? 0;

Запрос "SELECT SUM ([Amount])" вернет NULL для пустого списка. Но если вы используете LINQ, он ожидает, что "Sum (l => l.Amount)" вернет значение double, и это не позволит вам использовать оператор "??" для установки 0 для пустой коллекции.

Чтобы избежать этой ситуации, вам нужно заставить LINQ ожидать "double?". Вы можете сделать это, применив "(double?) L.Amount".

Это не влияет на запрос к SQL, но заставляет LINQ работать с пустыми коллекциями.