Максимальное возвращаемое значение, если пустой запрос


175

У меня есть этот запрос:

int maxShoeSize = Workers
    .Where(x => x.CompanyId == 8)
    .Max(x => x.ShoeSize);

Что будет, maxShoeSizeесли в компании 8 вообще не будет работников?

ОБНОВЛЕНИЕ:
Как я могу изменить запрос, чтобы получить 0, а не исключение?


Наор: вы слышали о LINQPad?
Митч Уит

3
Я не понимаю, почему вы спрашиваете: «Что будет maxShoeSize?» если вы уже попробовали это.
JWG

@jwg: Думаю, я хотел узнать, знаешь ли ты ответ :) В конце концов, у меня появился лучший способ сделать то, что я просил, и именно это я и имел в виду.
Наор

@Naor, это не игра в догадки. Я также хотел бы понизить первоначальный вопрос. Если вы знаете ответ, дайте его нам, иначе вы будете выглядеть ленивым. Только сейчас я собирался сделать тот же вопрос, и я готовлю всю информацию, включая сообщение об исключении.
Хуан Карлос Оропеза

Ответы:


298
int maxShoeSize = Workers.Where(x => x.CompanyId == 8)
                         .Select(x => x.ShoeSize)
                         .DefaultIfEmpty(0)
                         .Max();

Ноль в DefaultIfEmptyне нужен.


Работает :) Но на моем коде нужен был ноль в DefaultIfEmpty.
Карлос Тенорио Перес

1
Относительно первой части вопроса, на которую здесь не дан полный ответ: Согласно официальной документации , если универсальный тип пустой последовательности является ссылочным типом, вы получите null. В противном случае, согласно этому вопросу , вызов Max()пустой последовательности приводит к ошибке.
Раймунд Кремер

59

Я знаю, что это старый вопрос, и принятый ответ работает, но этот вопрос ответил на мой вопрос о том, приведет ли такой пустой набор к исключению или default(int)результату.

Однако принятый ответ, хотя он и работает, не является идеальным решением ИМХО, которое здесь не приводится. Таким образом, я предоставляю это в своем собственном ответе для выгоды любого, кто ищет это.

Оригинальный код ОП был:

int maxShoeSize = Workers.Where(x => x.CompanyId == 8).Max(x => x.ShoeSize);

Вот как я мог бы написать это, чтобы предотвратить исключения и предоставить результат по умолчанию:

int maxShoeSize = Workers.Where(x => x.CompanyId == 8).Max(x => x.ShoeSize as int?) ?? 0;

Это приводит к типу возвращаемого значения Maxфункции int?, который разрешает nullрезультат, а затем ??заменяет nullрезультат на 0.


РЕДАКТИРОВАТЬ
Чтобы пояснить что-то из комментариев, Entity Framework в настоящее время не поддерживает asключевое слово, поэтому способ написать его при работе с EF будет следующим:

int maxShoeSize = Workers.Where(x => x.CompanyId == 8).Max<[TypeOfWorkers], int?>(x => x.ShoeSize) ?? 0;

Поскольку имя [TypeOfWorkers]может быть длинным и утомительным для написания, я добавил метод расширения, чтобы выручить.

public static int MaxOrDefault<T>(this IQueryable<T> source, Expression<Func<T, int?>> selector, int nullValue = 0)
{
    return source.Max(selector) ?? nullValue;
}

Это только ручки int, но то же самое можно было бы сделать long, doubleили любой другой тип значения вам нужно. Использовать этот метод расширения очень просто: вы просто передаете свою функцию селектора и, необязательно, включаете значение, которое будет использоваться для нуля, по умолчанию равное 0. Таким образом, вышеприведенное можно переписать так:

int maxShoeSize = Workers.Where(x => x.CompanyId == 8).MaxOrDefault(x => x.ShoeSize);

Надеюсь, это поможет людям еще больше.


3
Отличное решение! Более популярный DefaultIfEmptyответ работает только тогда, когда Maxне проводится оценка.
McGuireV10

@ McGuireV10 Да, я обычно не люблю использовать Selectв качестве посредника, когда я просто собираюсь использовать агрегатную функцию, как Maxна результате. Я также думаю (я еще не проверял это), что сгенерированный SQL будет использовать дополнительный запрос на выборку, выполняя это, в то время как мой будет иметь дело с пустым набором, возвращая ноль. Спасибо за отзыв и отзыв! ;)
CptRobby

@ McGuireV10 Точно так же, если ShoeSizeна самом деле в соответствующей Uniformсущности, я бы не использовать Workers.Where(x => x.CompanyId == 8).Select(x => x.Uniform).Max(x => x.ShoeSize), вместо этого я бы просто держать всю оценку в Maxфункции: Workers.Where(x => x.CompanyId == 8).Max(x => x.Uniform.ShoeSize). Я предпочитаю использовать как можно меньше методов в своих запросах, чтобы позволить EF иметь наибольшую свободу при принятии решений о том, как эффективно создавать запросы. ;-)
CptRobby

1
Я не мог заставить это работать в EntityFramework. Кто-нибудь может пролить свет? (Использование техники DefaultIfEmpty сработало).
Мо Сиско

1
Обобщенная версия метода расширения:public static TResult MaxOrDefault<TElement, TResult>(this IQueryable<TElement> items, Expression<Func<TElement, TResult>> selector, TResult defaultValue = default) where TResult : struct => items.Select(selector).Max(item => (TResult?)item) ?? defaultValue;
относительно_лучая



10

Если это Linq to SQL, я не люблю его использовать, Any()потому что это приводит к нескольким запросам к SQL-серверу.

Если ShoeSizeэто поле не обнуляемо, то использование только the .Max(..) ?? 0не будет работать, но следующее будет:

int maxShoeSize = Workers.Where(x = >x.CompanyId == 8).Max(x => (int?)x.ShoeSize) ?? 0;

Это абсолютно не меняет испускаемый SQL, но он возвращает 0, если последовательность пуста, потому что он изменяет, Max()чтобы возвращать int?вместо вместо int.


4
int maxShoeSize=Workers.Where(x=>x.CompanyId==8)
    .Max(x=>(int?)x.ShoeSize).GetValueOrDefault();

(при условии, что ShoeSizeэто типа int)

Если Workersэто - DbSetили ObjectSetиз Entity Framework, ваш начальный запрос будет выдавать InvalidOperationException, но не будет жаловаться на пустую последовательность, а будет жаловаться, что материализованное значение NULL не может быть преобразовано в int.


2

Макс выбросит System.InvalidOperationException «Последовательность не содержит элементов»

class Program
{
    static void Main(string[] args)
    {
        List<MyClass> list = new List<MyClass>();

        list.Add(new MyClass() { Value = 2 });

        IEnumerable<MyClass> iterator = list.Where(x => x.Value == 3); // empty iterator.

        int max = iterator.Max(x => x.Value); // throws System.InvalidOperationException
    }
}

class MyClass
{
    public int Value;
}

2

Примечание: запрос с DefaultIfEmpty()может быть значительно медленнее . В моем случае это был простой запрос с .DefaultIfEmpty(DateTime.Now.Date).

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

Вывод: иногда обработка InvalidOperationExceptionможет быть лучшим выбором.


0

Вы можете использовать троичный внутри .Max()для обработки предиката и установить его значение;

// assumes Workers != null && Workers.Count() > 0
int maxShoeSize = Workers.Max(x => (x.CompanyId == 8) ? x.ShoeSize : 0);

Вам нужно будет обрабатывать Workersколлекцию как пустую / пустую, если это возможно, но это будет зависеть от вашей реализации.


0

Вы можете попробовать это:

int maxShoeSize = Workers.Where(x=>x.CompanyId == 8).Max(x => x.ShoeSize) ?? 0;

Я думаю, что это потерпит неудачу, так как Макс ожидает int, и он получает ноль; поэтому ошибка уже произошла до того, как вступил в силу оператор объединения нулей.
d219

0

Вы можете проверить, есть ли работники, прежде чем выполнять Max ().

private int FindMaxShoeSize(IList<MyClass> workers) {
   var workersInCompany = workers.Where(x => x.CompanyId == 8);
   if(!workersInCompany.Any()) { return 0; }
   return workersInCompany.Max(x => x.ShoeSize);
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.