Ответы:
Я знаю, что другие написали, почему вы используете один или другой, но я подумал, что я бы проиллюстрировал, почему вы НЕ должны использовать один, когда вы имеете в виду другой.
Примечание: В моем коде, я обычно использую FirstOrDefault()
и , SingleOrDefault()
но это другой вопрос.
Возьмем, к примеру, таблицу, которая хранится Customers
на разных языках с помощью составного ключа ( ID
, Lang
):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).First();
Этот код выше вводит возможную логическую ошибку (трудно отследить). Он вернет более одной записи (при условии, что у вас есть запись клиента на нескольких языках), но он всегда вернет только первую ... которая иногда может работать ... но не другие. Это непредсказуемо.
Поскольку ваше намерение состоит в том, чтобы вернуть одноразовое Customer
использование Single()
;
Следующее выдаст исключение (что вам и нужно в этом случае):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).Single();
Затем вы просто бьете себя по лбу и говорите себе ... ОЙ! Я забыл поле языка! Ниже приводится правильная версия:
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 && c.Lang == "en" ).Single();
First()
полезно в следующем сценарии:
DBContext db = new DBContext();
NewsItem newsitem = db.NewsItems.OrderByDescending( n => n.AddedDate ).First();
Он вернет ОДИН объект, и, поскольку вы используете сортировку, это будет самая последняя возвращаемая запись.
Использование, Single()
когда вы чувствуете, что оно должно явно возвращать 1 запись, поможет вам избежать логических ошибок.
customers.Where(predicate).Single()
customers.Single(predicate)
?
Single сгенерирует исключение, если найдет более одной записи, соответствующей критериям. Первая всегда выберет первую запись из списка. Если запрос возвращает только 1 запись, вы можете пойти с First()
.
Оба будут генерировать InvalidOperationException
исключение, если коллекция пуста. В качестве альтернативы вы можете использовать SingleOrDefault()
. Это не вызовет исключения, если список пуст
Не замужем()
Возвращает один конкретный элемент запроса
Когда используется : если ожидается ровно 1 элемент; не 0 или больше 1. Если список пуст или содержит более одного элемента, он выдаст исключение «Последовательность содержит более одного элемента»
SingleOrDefault ()
Возвращает один конкретный элемент запроса или значение по умолчанию, если результат не найден
Когда используется : когда ожидается 0 или 1 элемент. Он выдаст исключение, если список содержит 2 или более элементов.
Первый()
Возвращает первый элемент запроса с несколькими результатами.
Когда используется : когда ожидается 1 или более элементов, и вы хотите только первый. Он выдаст исключение, если список не содержит элементов.
FirstOrDefault ()
Возвращает первый элемент списка с любым количеством элементов или значение по умолчанию, если список пуст.
Когда используется : когда ожидается несколько элементов, и вы хотите только первый. Или список пуст, и вы хотите значение по умолчанию для указанного типа, так же, как
default(MyObjectType)
. Например: если указан тип списка,list<int>
он вернет первое число из списка или 0, если список пуст. Если это такlist<string>
, он вернет первую строку из списка или ноль, если список пуст.
First
когда ожидается 1 или более элементов , а не только «более 1» и FirstOrDefault
с любым количеством элементов.
Между этими двумя методами есть тонкая семантическая разница.
Используется Single
для извлечения первого (и единственного) элемента из последовательности, которая должна содержать один элемент и не более. Если в последовательности содержится больше одного элемента, ваш вызов Single
вызовет исключение, поскольку вы указали, что должен быть только один элемент.
Используется First
для извлечения первого элемента из последовательности, которая может содержать любое количество элементов. Если последовательность имеет больше, чем элемент, ваш вызовFirst
не вызовет исключения, поскольку вы указали, что вам нужен только первый элемент в последовательности, и вас не волнует, существует ли больше.
Если последовательность не содержит элементов, оба вызова метода вызовут исключения, поскольку оба метода ожидают присутствия хотя бы одного элемента.
Если вы не хотите специально создавать исключение в случае наличия более одного элемента, используйтеFirst()
.
Оба эффективны, возьмите первый пункт. First()
немного более эффективен, потому что он не проверяет наличие второго элемента.
Единственное отличие состоит в том, Single()
что в начале перечисления будет только один элемент, и будет выдано исключение, если их несколько. Вы используете, .Single()
если вы хотите, чтобы в этом случае было сгенерировано исключение .
Если я помню, Single () проверяет, есть ли другой элемент после первого (и выдает исключение, если это так), тогда как First () останавливается после его получения. Оба выдают исключение, если последовательность пуста.
Лично я всегда использую First ().
Что касается производительности: мы с коллегой обсуждали производительность Single vs First (или SingleOrDefault vs FirstOrDefault), и я спорил о том, что First (или FirstOrDefault) будет быстрее и улучшит производительность (я все о создании нашего приложения бежать быстрее).
Я прочитал несколько сообщений о переполнении стека, которые обсуждают это. Некоторые говорят, что при использовании First вместо Single наблюдается небольшой прирост производительности. Это связано с тем, что First просто вернет первый элемент, в то время как Single должен просканировать все результаты, чтобы убедиться в отсутствии дубликата (т. Е. Если он найдет элемент в первой строке таблицы, он все равно будет сканировать все остальные строки, чтобы убедитесь, что нет второго значения, соответствующего условию, которое затем выдаст ошибку). Я чувствовал, что был на твердой почве, когда «First» был быстрее, чем «Single», поэтому я решил доказать это и положить конец дискуссии.
Я настроил тест в своей базе данных и добавил 1 000 000 строк ID UniqueIdentifier Foreign UniqueIdentifier Info nvarchar (50) (заполненный строками с номерами от «0» до «999,9999»)
Я загрузил данные и установил ID в качестве поля первичного ключа.
Используя LinqPad, моя цель состояла в том, чтобы показать, что если бы вы искали значение в «Foreign» или «Info» с помощью Single, это было бы намного хуже, чем с помощью First.
Я не могу объяснить результаты, которые я получил. Почти в каждом случае использование Single или SingleOrDefault было немного быстрее. Это не имеет никакого логического смысла для меня, но я хотел поделиться этим.
Пример: я использовал следующие запросы:
var q = TestTables.First(x=>x.Info == "314638") ;
//Vs.
Var q = TestTables.Single(x=>x.Info =="314638") ; //(this was slightly faster to my surprise)
Я пробовал похожие запросы в поле 'Foreign' key, которое не было проиндексировано, полагая, что First быстрее, но Single всегда немного быстрее в моих тестах.
Они разные. Оба они утверждают, что набор результатов не является пустым, но одиночный также утверждает, что существует не более 1 результата. Я лично использую Single в тех случаях, когда я ожидаю, что будет только 1 результат только потому, что получение более 1 результата назад является ошибкой и, вероятно, должно рассматриваться как таковое.
Вы можете попробовать простой пример, чтобы получить разницу. Исключение будет сгенерировано в строке 3;
List<int> records = new List<int>{1,1,3,4,5,6};
var record = records.First(x => x == 1);
record = records.Single(x => x == 1);
Записи в сущности Сотрудник:
Employeeid = 1
: Только один сотрудник с этим идентификатором
Firstname = Robert
: Более одного сотрудника с таким именем
Employeeid = 10
: Нет сотрудника с таким ID
Теперь необходимо понять, что Single()
и что First()
значить в деталях.
Не замужем()
Single () используется для возврата единственной записи, которая уникально существует в таблице, поэтому следующий запрос вернет сотрудника, employeed =1
у которого только один сотрудник Employeed
равен 1. Если у нас есть две записи, EmployeeId = 1
он выдает ошибку (см. ошибка ниже во втором запросе, где мы используем пример для Firstname
.
Employee.Single(e => e.Employeeid == 1)
Выше будет возвращать одну запись, которая имеет 1 employeeId
Employee.Single(e => e.Firstname == "Robert")
Выше приведено исключение, потому что в таблице есть несколько записей FirstName='Robert'
. Исключение будет
InvalidOperationException: последовательность содержит более одного элемента
Employee.Single(e => e.Employeeid == 10)
Это снова вызовет исключение, поскольку для id = 10 не существует записей. Исключение будет
InvalidOperationException: последовательность не содержит элементов.
Поскольку EmployeeId = 10
это возвратит нуль, но, поскольку мы используем Single()
это, выдаст ошибку. Для обработки нулевой ошибки мы должны использоватьSingleOrDefault()
.
Первый()
First () возвращает из нескольких записей соответствующие записи, отсортированные в порядке возрастания в соответствии с birthdate
тем, что он возвращает «Роберт», который является самым старым.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Firstname == "Robert")
Выше должен вернуть самый старый, Роберт согласно ДОБ.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Employeeid == 10)
Выше будет выдано исключение, так как не существует записи для id = 10. Чтобы избежать нулевого исключения, мы должны использоватьFirstOrDefault()
вместоFirst()
.
Примечание: мы можем использовать только First()
/Single()
когда мы абсолютно уверены, что он не может вернуть нулевое значение.
В обеих функциях используйте SingleOrDefault () ИЛИ FirstOrDefault (), который будет обрабатывать нулевое исключение, в случае отсутствия записи он вернет ноль.