Я использую ядро ef в своем проекте API ядра asp. Мне нужно найти индекс наивысшего порядка.
Пример:
Таблица данных: Id, ForeignId, OrderIndex
Итак, я делаю:
var highestOrderIndex = await _context
.ExampleDbSet
.Where(x =>
x.ForeignId == foreignId)
.MaxAsync(x =>
x.OrderIndex);
Проблема в том, что пример набора db содержит 0 элементов. Это вызовет исключение: Sequence contains no element.
Есть ли элегантный способ сделать это? Потому что я не хочу получать все элементы из базы данных. И это должно быть асинхронно.
Спасибо





Вы можете узнать, существуют ли какие-либо записи, и если они есть, то определить макс. Что-то вроде этого:
var query = _context.ExampleDbSet
.Where(x => x.ForeignId == foreignId);
var itemsExist = await query.AnyAsync();
int maxOrderIndex = 0;
if (itemsExist)
{
maxOrderIndex = await query.MaxAsync(x => x.OrderIndex);
}
Здесь вам не нужно будет извлекать все элементы из базы данных, только проверьте, существует ли запись, что намного быстрее, и вы также можете сохранить метод асинхронным.
да, AnyAsync будет еще лучше. Я обновил свой ответ, чтобы использовать его вместо CountAsync.
Вы также можете разобрать запрос where в anyasync :)
выполнение его так, как показано в ответе, не требует, чтобы мы дважды писали одну и ту же логику лямбда-выражения фильтрации, поэтому, если она изменится, есть место для ее изменения в
Я не рекомендую это делать, так как он будет выполнять два отдельных запроса к базе данных (как указано в подтвержденном ответе).
Выполнение AnyAsync, а затем MaxAsync приведет к двум отдельным вызовам базы данных. Вы можете сжать его в единицу, убедившись, что последовательность содержит минимальное значение "по умолчанию". Это полезный трюк везде, где вы используете методы Linq Max / Min, а не только в коде базы данных:
context.ExampleDbSet
.Where(w => w.ForeignId == foreignId)
.Select(s => s.OrderIndex)
.Concat(new[] { 0 })
.MaxAsync();
Выполняется ли max async в базе данных с помощью SQL-запроса? Есть ли функция sql, которая позволяет что-то конкатенировать?
На самом деле существует довольно элегантный (и более производительный по сравнению с предложенным в другом ответе, потому что он выполняет только один запрос к базе данных), используя тот факт, что агрегированные методы, такие как Min, Max, вызывают исключение Sequence contains no element только при использовании с перегрузками, не допускающими значения NULL, но Перегрузки, допускающие значение NULL, вместо этого просто возвращают null.
Итак, все, что вам нужно, - это преобразовать тип свойства, не допускающий значение NULL, к соответствующему типу, допускающему значение NULL. Например, если тип OrderIndex - int, единственное изменение в вашем запросе может быть
.MaxAsync(x => (int?)x.OrderIndex);
Обратите внимание, что это также изменит тип принимающей переменной highestOrderIndex на int?. Вы можете проверить наличие null и отреагировать соответствующим образом, или вы можете просто объединить вызов агрегированной функции с оператором ?? и указать какое-то значение по умолчанию, например
var highestOrderIndex = (await _context.ExampleDbSet
.Where(x => x.ForeignId == foreignId)
.MaxAsync(x => (int?)x.OrderIndex)) ?? -1; // or whatever "magic" number works for you
Другой способ с немного лучшей производительностью, чем MaxAsync, если значение по умолчанию - это то, которое вы хотите получить, если результатов нет:
var highestOrderIndex = await _context.ExampleDbSet
.Where(x => x.ForeignId == foreignId)
.OrderByDescending(x => x.OrderIndex)
.Select(x => x.OrderIndex)
.FirstOrDefaultAsync();
TOP быстрее агрегатных функций, посмотрите план выполнения в своем SQL Server.
Вы можете использовать DefaultIfEmpty и Select перед MaxAsync.
var highestOrderIndex = await _context
.ExampleDbSet
.Where(x =>
x.ForeignId == foreignId)
.Select(x => x.OrderIndex)
.DefaultIfEmpty() // default is 0 if OrderIndex is int or long
// .DefaultIfEmpty(-1) // default is -1
.MaxAsync();
По какой-то непонятной причине при этом все еще выдается сообщение «Последовательность не содержит элементов». И синхронная, и асинхронная версия Max.
В порядке. Но я буду использовать AnyAsync ()