LINQ: когда использовать SingleOrDefault или FirstOrDefault () с критериями фильтрации - PullRequest
459 голосов
/ 17 ноября 2009

Рассмотрим IEnumerable методы расширения 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?

Ответы [ 14 ]

533 голосов
/ 21 января 2011

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

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

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

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

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

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

Вывод:

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

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

424 голосов
/ 17 ноября 2009

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

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

227 голосов
/ 17 ноября 2009

Есть

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

между двумя.

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

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

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

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

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

Заключение

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

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

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

70 голосов
/ 06 сентября 2011

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

12 голосов
/ 10 ноября 2017

Для LINQ -> SQL:

SingleOrDefault

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

FirstOrDefault

  • сгенерирует запрос типа «выберите топ 1 * от пользователей, где userid = 1»
  • Выберите первые подходящие строки
  • Используйте, если вы выбираете данные на основе столбца с не первичным / уникальным ключом
10 голосов
/ 17 ноября 2009

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

5 голосов
/ 01 сентября 2010

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

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

4 голосов
/ 17 ноября 2009

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

выберите по ID == 5: здесь можно использовать SingleOrDefault, потому что вы ожидаете одну [или ни одну] сущность, если вы получили более одной сущности с ID 5, что-то не так и определенно заслуживает исключения.

при поиске людей, чье имя равно «Бобби», их может быть несколько (вполне возможно, я бы подумал), поэтому вам не следует ни использовать «Единый», ни «Первый», просто выберите с помощью операции «Где» (если «Бобби») возвращает слишком много объектов, пользователь должен уточнить свой поиск или выбрать один из возвращенных результатов)

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

3 голосов
/ 28 августа 2016

Оба являются операторами элементов, и они используются для выбора одного элемента из последовательности. Но между ними есть небольшая разница. Оператор 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();
3 голосов
/ 17 ноября 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...