Подтвердить что ты не робот

LINQ: когда использовать SingleOrDefault и FirstOrDefault() с критериями фильтрации

Рассмотрим методы IEnumerable extension SingleOrDefault() и FirstOrDefault()

Документы MSDN, которые SingleOrDefault:

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

тогда как FirstOrDefault из MSDN (предположительно при использовании OrderBy() или OrderByDescending() или вообще ничего),

Возвращает первый элемент последовательности

Рассмотрим несколько примеров запросов, не всегда понятно, когда использовать эти два метода:

var someCust = db.Customers
.SingleOrDefault(c=>c.ID == 5); //unlikely(?) to be more than one, but technically COULD BE

var bobbyCust = db.Customers
.FirstOrDefault(c=>c.FirstName == "Bobby"); //clearly could be one or many, so use First?

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or does it matter?

Вопрос

Какие соглашения вы придерживаетесь или предлагаете при принятии решения использовать SingleOrDefault() и FirstOrDefault() в ваших запросах LINQ?

4b9b3361

Ответ 1

Всякий раз, когда вы используете SingleOrDefault, вы четко заявляете, что запрос должен привести не более одного результата. С другой стороны, когда используется FirstOrDefault, запрос может вернуть любое количество результатов, но вы утверждаете, что вам нужен только первый.

Я лично нахожу семантику очень разной, и использование соответствующей, в зависимости от ожидаемых результатов, улучшает читабельность.

Ответ 2

Если ваш результирующий набор возвращает 0 записей:

  • SingleOrDefault возвращает значение по умолчанию для типа (например, значение по умолчанию для int равно 0)
  • FirstOrDefault возвращает значение по умолчанию для типа

Если результат результата возвращает 1 запись:

  • SingleOrDefault возвращает эту запись
  • FirstOrDefault возвращает эту запись

Если ваш результирующий набор возвращает много записей:

  • SingleOrDefault генерирует исключение
  • FirstOrDefault возвращает первую запись

Заключение:

Если вы хотите создать исключение, если набор результатов содержит много записей, используйте SingleOrDefault.

Если вы всегда хотите 1 запись, независимо от того, что содержит набор результатов, используйте FirstOrDefault

Ответ 3

Существует

  • семантическая разность
  • разница в производительности

между ними.

Семантическая разница:

  • FirstOrDefault возвращает первый элемент потенциально множественного (или по умолчанию, если он не существует).
  • SingleOrDefault предполагает, что есть один элемент и возвращает его (или по умолчанию, если он не существует). Несколько пунктов являются нарушением контракта, исключение выдается.

Разница в производительности

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

  • SingleOrDefault необходимо проверить, существует ли только один элемент и, следовательно, он всегда выполняет повторение всего перечислимого. Если быть точным, он выполняет итерацию, пока не найдет второй элемент и выбрасывает исключение. Но в большинстве случаев нет второго элемента.

Заключение

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

  • Используйте SingleOrDefault, если вам не нужно слишком много заботиться о производительности и хотите, чтобы предположение об одном элементе было понятным для читателя и проверено во время выполнения.

На практике часто используется First/FirstOrDefault даже в тех случаях, когда вы принимаете один элемент, чтобы повысить производительность. Вы все равно должны помнить, что Single/SingleOrDefault может улучшить читаемость (потому что в нем указано предположение о единственном элементе) и стабильность (потому что он ее проверяет) и использовать его соответствующим образом.

Ответ 4

Никто не упомянул, что FirstOrDefault, переведенный в SQL, делает запись TOP 1, а SingleOrDefault делает TOP 2, потому что ему нужно знать, что существует более 1 записи.

Ответ 5

Для LINQ → SQL:

SingleOrDefault

  • будет генерировать запрос типа "select *" у пользователей, где userid = 1 "
  • Выберите соответствующую запись, выдает исключение, если найдено несколько записей
  • Использовать, если вы извлекаете данные на основе первичного/уникального столбца ключа

FirstOrDefault

  • будет генерировать запрос типа "select top 1 * от пользователей, где userid = 1"
  • Выберите первые соответствующие строки
  • Использовать, если вы извлекаете данные на основе не первичного/уникального столбца ключа

Ответ 6

Я использую SingleOrDefault в ситуациях, когда моя логика диктует, что будет либо нулевой, либо один результат. Если есть больше, это может быть ошибкой.

Ответ 7

SingleOrDefault: вы говорите, что "самое большее" есть один элемент, соответствующий запросу или по умолчанию FirstOrDefault: вы говорите, что есть "по крайней мере" один элемент, соответствующий запросу или по умолчанию

Скажите это громко в следующий раз, когда вам нужно выбрать, и вы, вероятно, будете выбирать разумно.:)

Ответ 8

В ваших случаях я бы использовал следующее:

выберите по ID == 5: это ОК, чтобы использовать SingleOrDefault здесь, потому что вы ожидаете одного [или ни одного] объекта, если у вас есть более одного объекта с идентификатором 5, есть что-то неправильное и безусловно исключительное достоинство.

при поиске людей, чье первое имя равно "Бобби", может быть несколько (возможно, я думаю), поэтому не следует использовать Single или First, просто выберите с помощью Where-operation (если "Bobby" возвращает слишком много объектов, пользователь должен уточнить свой поиск или выбрать один из возвращенных результатов)

порядок по дате создания также должен выполняться с помощью операции "Где" (вряд ли будет иметь только один объект, сортировка не будет очень полезна;) это, однако, означает, что вы хотите, чтобы ВСЕ сущности сортировались - если вы хотите только ОДИН, используйте FirstOrDefault, Single будет бросать каждый раз, если вы получили более одного объекта.

Ответ 9

В последнем примере:

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or doesn't matter?

Да, да. Если вы попытаетесь использовать SingleOrDefault(), и результат запроса будет больше, чем запись, вы получите и исключение. Единственный раз, когда вы можете безопасно использовать SingleOrDefault(), - это когда вы ожидаете только 1 и только 1 результат...

Ответ 10

Оба являются операторами элементов, и они используются для выбора одного элемента из последовательности. Но между ними существует незначительная разница. Оператор SingleOrDefault() генерирует исключение, если более чем одному элементу удовлетворяется условие, когда в качестве FirstOrDefault() не будет выбрано исключение для одного и того же. Вот пример.

List<int> items = new List<int>() {9,10,9};
//Returns the first element of a sequence after satisfied the condition more than one elements
int result1 = items.Where(item => item == 9).FirstOrDefault();
//Throw the exception after satisfied the condition more than one elements
int result3 = items.Where(item => item == 9).SingleOrDefault();

Ответ 11

Итак, как я понимаю, SingleOrDefault будет хорошо, если вы запрашиваете данные, которые гарантированно будут уникальными, то есть будут принудительно использованы ограничениями DB, такими как первичный ключ.

Или есть лучший способ запроса первичного ключа.

Предполагая, что мой TableAcc имеет

AccountNumber - Primary Key, integer
AccountName
AccountOpenedDate
AccountIsActive
etc.

и я хочу запросить AccountNumber 987654, я использую

var data = datacontext.TableAcc.FirstOrDefault(obj => obj.AccountNumber == 987654);

Ответ 12

Я запросил Google для использования различных методов на GitHub. Это делается путем запуска поискового запроса Google для каждого метода и ограничения запроса доменом github.com и расширением файла .cs с помощью запроса "site: github.com file: cs..."

Похоже, что методы First * используются чаще, чем методы Single *.

| Method               | Results |
|----------------------|---------|
| FirstAsync           |     315 |
| SingleAsync          |     166 |
| FirstOrDefaultAsync  |     357 |
| SingleOrDefaultAsync |     237 |
| FirstOrDefault       |   17400 |
| SingleOrDefault      |    2950 |

Ответ 13

Одна вещь, которая пропущена в ответах....

Если имеется несколько результатов, FirstOrDefault без порядка может возвращать разные результаты, на основе которых когда-либо использовалась серверная стратегия.

Лично я не могу видеть FirstOrDefault в коде, потому что для меня это говорит, что разработчику не нравились результаты. С заказом, хотя он может быть полезен как способ обеспечения последней/самой ранней. Мне пришлось исправить множество проблем, вызванных неосторожными разработчиками, использующими FirstOrDefault.

Ответ 14

Я не понимаю, почему вы используете FirstOrDefault(x=> x.ID == key), когда это может получить результаты намного быстрее, если вы используете Find(key). Если вы запрашиваете ключ Primary таблицы, эмпирическое правило всегда использует Find(key). FirstOrDefault следует использовать для файлов предикатов типа (x=> x.Username == username) и т.д.

это не заслужило нисходящего потока, поскольку заголовок вопроса не был специфичен для linq для DB или Linq для List/IEnumerable и т.д.