Respostas:
Sei que outros escreveram por que você usa um ou outro, mas pensei em ilustrar por que você NÃO deveria usar um, quando quis dizer o outro.
Nota: No meu código, eu normalmente usar FirstOrDefault()
e SingleOrDefault()
mas isso é uma questão diferente.
Tome, por exemplo, uma tabela que armazena Customers
em diferentes idiomas usando uma chave composta ( ID
, Lang
):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).First();
Este código acima apresenta um possível erro lógico (difícil de rastrear). Ele retornará mais de um registro (supondo que você tenha o registro do cliente em vários idiomas), mas sempre retornará apenas o primeiro ... que pode funcionar algumas vezes ... mas não em outros. É imprevisível.
Como sua intenção é retornar um Customer
uso único Single()
;
O seguinte geraria uma exceção (que é o que você deseja neste caso):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).Single();
Então, você simplesmente se bate na testa e diz para si mesmo ... OOPS! Eu esqueci o campo da linguagem! A seguir está a versão correta:
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 && c.Lang == "en" ).Single();
First()
é útil no seguinte cenário:
DBContext db = new DBContext();
NewsItem newsitem = db.NewsItems.OrderByDescending( n => n.AddedDate ).First();
Ele retornará UM objeto e, como você está usando a classificação, será o registro mais recente retornado.
Usar Single()
quando você achar que ele deve retornar sempre 1 registro explicitamente ajudará a evitar erros de lógica.
customers.Where(predicate).Single()
customers.Single(predicate)
?
Single lançará uma exceção se encontrar mais de um registro que corresponda aos critérios. Primeiro sempre selecionará o primeiro registro da lista. Se a consulta retornar apenas 1 registro, você poderá prosseguir First()
.
Ambos lançarão uma InvalidOperationException
exceção se a coleção estiver vazia. Alternativamente, você pode usar SingleOrDefault()
. Isso não emitirá uma exceção se a lista estiver vazia
Solteiro()
Retorna um único elemento específico de uma consulta
Quando usar : se exatamente 1 elemento é esperado; não 0 ou mais que 1. Se a lista estiver vazia ou tiver mais de um elemento, será gerada uma exceção "Sequência contém mais de um elemento"
SingleOrDefault ()
Retorna um único elemento específico de uma consulta ou um valor padrão se nenhum resultado encontrado
Quando usar : quando são esperados 0 ou 1 elementos. Irá gerar uma exceção se a lista tiver 2 ou mais itens.
Primeiro()
Retorna o primeiro elemento de uma consulta com vários resultados.
Quando usar : quando 1 ou mais elementos são esperados e você deseja apenas o primeiro. Irá gerar uma exceção se a lista não contiver elementos.
FirstOrDefault ()
Retorna o primeiro elemento de uma lista com qualquer quantidade de elementos ou um valor padrão se a lista estiver vazia.
Quando usar : quando vários elementos são esperados e você deseja apenas o primeiro. Ou a lista está vazia e você deseja um valor padrão para o tipo especificado, o mesmo que
default(MyObjectType)
. Por exemplo: se o tipo de lista for,list<int>
ele retornará o primeiro número da lista ou 0 se a lista estiver vazia. Se forlist<string>
, retornará a primeira string da lista ou null se a lista estiver vazia.
First
quando 1 ou mais elementos são esperados , não apenas "mais que 1" e FirstOrDefault
com qualquer quantidade de elementos.
Há uma diferença sutil e semântica entre esses dois métodos.
Use Single
para recuperar o primeiro (e único) elemento de uma sequência que deve conter um elemento e não mais. Se a sequência tiver mais do que um elemento, sua chamada Single
causará uma exceção, uma vez que você indicou que deveria haver apenas um elemento.
Use First
para recuperar o primeiro elemento de uma sequência que pode conter qualquer número de elementos. Se a sequência tiver mais do que um elemento, sua chamada First
não causará uma exceção, uma vez que você indicou que só precisa do primeiro elemento da sequência e não se importa se existir mais.
Se a sequência não contiver elementos, ambas as chamadas de método farão com que exceções sejam lançadas, pois os dois métodos esperam que pelo menos um elemento esteja presente.
Se você não deseja especificamente uma exceção lançada no caso de haver mais de um item, useFirst()
.
Ambos são eficientes, pegue o primeiro item. First()
é um pouco mais eficiente, porque não se preocupa em verificar se há um segundo item.
A única diferença é que Single()
espera que exista apenas um item na enumeração e lançará uma exceção se houver mais de um. Você usa .Single()
se deseja especificamente uma exceção lançada neste caso.
Se bem me lembro, Single () verifica se existe outro elemento após o primeiro (e lança uma exceção, se for o caso), enquanto First () para após obtê-lo. Ambos lançam uma exceção se a sequência estiver vazia.
Pessoalmente, eu sempre uso First ().
Com relação ao desempenho: um colega de trabalho e eu estávamos discutindo o desempenho do Single vs First (ou SingleOrDefault vs FirstOrDefault), e estava argumentando pelo ponto de que First (ou FirstOrDefault) seria mais rápido e melhoraria o desempenho (eu pretendo criar nosso aplicativo correr mais rápido).
Eu li vários posts no Stack Overflow que debatem isso. Alguns dizem que há pequenos ganhos de desempenho usando First em vez de Single. Isso ocorre porque o First retornaria o primeiro item, enquanto o Single deve varrer todos os resultados para garantir que não haja uma duplicata (ou seja: se ele encontrasse o item na primeira linha da tabela, ele ainda varria todas as outras linhas para verifique se não existe um segundo valor que corresponda à condição que, em seguida, geraria um erro). Eu senti como se estivesse em terreno sólido, com o “Primeiro” sendo mais rápido que o “Solteiro”, então decidi provar isso e encerrar o debate.
Configurei um teste no meu banco de dados e adicionei 1.000.000 de linhas de ID UniqueIdentifier Foreign UniqueIdentifier Info nvarchar (50) (preenchido com cadeias de números "0" a "999,9999"
Carreguei os dados e defini o ID como um campo de chave primária.
Usando o LinqPad, meu objetivo era mostrar que, se você pesquisasse um valor em 'Estrangeiro' ou 'Informações' usando Único, isso seria muito pior do que usar Primeiro.
Não sei explicar os resultados que obtive. Em quase todos os casos, o uso de Single ou SingleOrDefault era um pouco mais rápido. Isso não faz nenhum sentido lógico para mim, mas eu queria compartilhar isso.
Ex: usei as seguintes consultas:
var q = TestTables.First(x=>x.Info == "314638") ;
//Vs.
Var q = TestTables.Single(x=>x.Info =="314638") ; //(this was slightly faster to my surprise)
Tentei consultas semelhantes no campo-chave 'Estrangeiro', que não estava indexado, pensando que o First seria mais rápido, mas o Single sempre foi um pouco mais rápido nos meus testes.
Eles são diferentes. Ambos afirmam que o conjunto de resultados não está vazio, mas único também afirma que não há mais de 1 resultado. Eu pessoalmente uso o Single nos casos em que espero apenas um resultado, apenas porque obter mais de um resultado é um erro e provavelmente deve ser tratado como tal.
Você pode tentar um exemplo simples para obter a diferença. A exceção será lançada na linha 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);
Os registros na entidade Funcionário:
Employeeid = 1
: Apenas um funcionário com esse ID
Firstname = Robert
: Mais de um funcionário com esse nome
Employeeid = 10
: Nenhum funcionário com este ID
Agora é necessário entender o que Single()
e o First()
significado em detalhes.
Solteiro()
Single () é usado para retornar um único registro que existe exclusivamente em uma tabela; portanto, a consulta abaixo retornará o Funcionário cuja, employeed =1
porque temos apenas um Funcionário cuja Employeed
é 1. Se tivermos dois registros EmployeeId = 1
, isso gera um erro (consulte a seção erro abaixo na segunda consulta para a qual estamos usando um exemplo Firstname
.
Employee.Single(e => e.Employeeid == 1)
O exemplo acima retornará um único registro, que possui 1 employeeId
Employee.Single(e => e.Firstname == "Robert")
O exemplo acima lançará uma exceção porque há registros múltiplos na tabela para FirstName='Robert'
. A exceção será
InvalidOperationException: Sequence contém mais de um elemento
Employee.Single(e => e.Employeeid == 10)
Isso, novamente, lançará uma exceção porque não existe registro para id = 10. A exceção será
InvalidOperationException: Sequence não contém elementos.
Pois EmployeeId = 10
ele retornará nulo, mas como estamos usando Single()
, gerará um erro. Para lidar com erro nulo, devemos usar SingleOrDefault()
.
Primeiro()
First () retorna de vários registros os registros correspondentes classificados em ordem crescente, de acordo com birthdate
o que retornará 'Robert', que é o mais antigo.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Firstname == "Robert")
Acima deve retornar o mais antigo, Robert conforme DOB.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Employeeid == 10)
Acima lançará uma exceção, pois não existe registro para id = 10. Para evitar uma exceção nula, devemos usar em FirstOrDefault()
vez de First()
.
Nota: Podemos usar somente First()
/ Single()
quando tivermos certeza absoluta de que ele não pode retornar um valor nulo.
Nas duas funções, use SingleOrDefault () OR FirstOrDefault () que manipulará uma exceção nula; no caso de nenhum registro encontrado, ele retornará nulo.