Código Linq para selecionar um item

105

Eu me pego escrevendo muito código como este para selecionar um item que corresponda

var item = (from x in Items where x.Id == 123 select x).First();

Existe uma maneira mais limpa de fazer isso ou é o mais conciso que vou ser?

EDIT: Deveria ter dito "maneira mais limpa usando sintaxe linq". Eu já estava ciente da sintaxe lambda e está começando a parecer que, na verdade, é a única maneira. Eu recebi algumas informações úteis, então, obrigado a todos que responderam.

Mikey Hogarth
fonte
5
Pessoalmente, eu evito Single()e SingleOrDefault()SE eu souber que os dados já são únicos (por exemplo, de um banco de dados que tem essa restrição, etc), pois o Single()obriga a examinar o resto da lista para encontrar uma possível duplicata, mas sou eu. Se você precisar reforçar sua singularidade neste ponto, use Single()family, se não, use First()family.
James Michael Hare,

Respostas:

176

Depende de quanto você gosta da sintaxe da consulta linq, você pode usar os métodos de extensão diretamente como:

var item = Items.First(i => i.Id == 123);

E se você não quiser lançar um erro se a lista estiver vazia, use o FirstOrDefaultque retorna o valor padrão para o tipo de elemento ( nullpara tipos de referência):

var item = Items.FirstOrDefault(i => i.Id == 123);

if (item != null)
{
    // found it
}

Single()e SingleOrDefault()também pode ser usado, mas se você está lendo de um banco de dados ou algo que já garante exclusividade, eu não me incomodaria, pois tem que verificar a lista para ver se há duplicatas e lançamentos. First()e FirstOrDefault()param na primeira partida, para serem mais eficientes.

Da família First()e Single(), é aqui que eles jogam:

  • First() - lança se vazio / não encontrado, não lança se duplicado
  • FirstOrDefault() - retorna o padrão se vazio / não encontrado, não lança se duplicado
  • Single() - lança se vazio / não encontrado, lança se houver duplicata
  • SingleOrDefault() - retorna o padrão se vazio / não encontrado, lança se houver duplicata
James Michael Hare
fonte
1
Acho que estão faltando dois sinais de igual aqui. Deve seri.Id == 123
davehale23
18

FirstOrDefault ou SingleOrDefault pode ser útil, dependendo do seu cenário e se você deseja lidar com a existência de zero ou mais de uma correspondência:

FirstOrDefault: Retorna o primeiro elemento de uma sequência ou um valor padrão se nenhum elemento for encontrado.

SingleOrDefault: Retorna o único elemento de uma sequência ou um valor padrão se a sequência estiver vazia; este método lança uma exceção se houver mais de um elemento na sequência

Eu não sei como isso funciona em uma consulta linq 'de', mas na sintaxe lambda se parece com isto:

var item1 = Items.FirstOrDefault(x => x.Id == 123);
var item2 = Items.SingleOrDefault(x => x.Id == 123);
Stuartd
fonte
qual é o mais eficiente em termos de tempo de DB? Se eu tivesse um varchar, eu queria uma correspondência exata, mas é possível que não haja nenhuma?
Piotr Kula
2
A resposta aceita aborda isso por completo, mas essencialmente FirstOrDefault para assim que encontra uma correspondência, mas SingleOrDefault deve examinar toda a lista para garantir que haja exatamente uma correspondência.
stuartd
12

Estes são os métodos preferidos:

var item = Items.SingleOrDefault(x => x.Id == 123);

Ou

var item = Items.Single(x => x.Id == 123);
James Hill
fonte
Obrigado - então nesta situação não há notação linq e eu preciso usar lambdas?
Mikey Hogarth,
Isso é muito bom. Na verdade, não usei muito o linq, então não tenho certeza se estava ciente desses métodos.
wageoghe
1
O método único verifica se o valor de retorno é exclusivo. Portanto, se sua coleção for grande, isso pode demorar muito. O primeiro método apenas retorna o primeiro elemento que corresponde ao predicado.
meziantou
A resposta de @wageoghe James não usa linq - os métodos Single e SingleOrDefault fazem parte da implementação de IEnumerable.
Mikey Hogarth,
12

Só para facilitar a vida de alguém, a consulta linq com expressão lambda

(from x in Items where x.Id == 123 select x).FirstOrDefault();

resulta em uma consulta SQL com um select top (1)nele.

Amal
fonte
9

Isso pode ser melhor condensado nisto.

var item = Items.First(x => x.Id == 123);

No momento, sua consulta está coletando todos os resultados (e pode haver mais de um) dentro do enumerável e, em seguida, pegando o primeiro desse conjunto, fazendo mais trabalho do que o necessário.

Single / SingleOrDefault valem a pena, mas apenas se você quiser iterar por toda a coleção e verificar se a correspondência é única, além de selecionar essa correspondência. First / FirstOrDefault apenas pegará a primeira correspondência e sairá, independentemente de quantas duplicatas realmente existam.

Chris Hannon
fonte
4

Você pode usar a sintaxe do método de extensão:

var item = Items.Select(x => x.Id == 123).FirstOrDefault();

Fora isso, não tenho certeza de quão mais conciso você pode ser, sem talvez escrever seus próprios métodos de extensão especializados "First" e "FirstOrDefault".

wageoghe
fonte
Não acho que seja esse o comportamento pretendido. Esta instrução Select retorna (na verdade não, mas seleciona para retorno) uma coleção de bool, um para cada elemento de Items, o primeiro do que é retornado como item, que se torna simplesmente um bool. Use a solução de Chris Hannon
Leonardo Daga
Talvez você queira dizer Whereao contrário de Select, que já foi declarado, mas essa resposta está incorreta. Selecionar em c # altera os resultados para IEnumerable <bool>, então você está obtendo um boolpara o primeiro itemx.Id == 123
bradlis7
2

Vou te contar o que funcionou para mim:

int id = int.Parse(insertItem.OwnerTableView.DataKeyValues[insertItem.ItemIndex]["id_usuario"].ToString());

var query = user.First(x => x.id_usuario == id);
tbUsername.Text = query.username;
tbEmail.Text = query.email;
tbPassword.Text = query.password;

Meu id é a linha que eu quero consultar, neste caso eu peguei de um radGrid, então usei para consultar, mas essa consulta retorna uma linha, então você pode atribuir os valores que você obteve da consulta para a caixa de texto, ou qualquer coisa , Tive que atribuí-los à caixa de texto.

G Jeny Ramirez
fonte