LINQ  -  FirstOrDefaultAsync, SingleOrDefaultAsync e FindAsync


 Hoje vamos comparar os métodos de extensão FirstOrDefaultAsync, SingleOrDefaultAsync e FindAsync usados em consultas LINQ com o EF Core.

Atualmente podemos usar três abordagens diferentes para obter resultados usando consultas LINQ com o EF Core, para isso vamos considerar os seguintes métodos de extensão FirstOrDefaultAsync(), SingleOrDefaultAsync() e FindAsync(). 


1- DbSet.FindAsync - Localiza de forma assíncrona uma entidade com os valores de chave primária fornecidos.


 
   
var item = await _context.Categorias.FindAsync(id);     
 

Se uma entidade com os valores de chave primária existir no contexto, ela será retornada imediatamente sem fazer uma solicitação para o repositório. Caso contrário, uma solicitação é feita ao repositório para uma entidade com os valores de chave primária fornecidos e essa entidade, se encontrada, é anexada ao contexto e retornada. Se nenhuma entidade for encontrada no contexto ou na fonte de dados, um NULL será retornado.

Observe que este método precisa usar a chave primária e que ele vai ser mais rápido se você tiver certeza de que a entidade esta sendo rastreada pelo contexto do EF Core.

Este método possui uma sobrecarga onde podemos um CancellationToken para cancelar a operação enquanto aguardamos a conclusão da tarefa.

2- FirstOrDefaultAsync - Retorna de forma assíncrona o primeiro elemento de uma sequência que satisfaça uma condição especificada ou um valor padrão se nenhum elemento desse tipo for encontrado.


 
 var item1 = await _context.Categorias.FirstOrDefaultAsync(c => c.CategoriaId == id);    
      

Este método vai retornar o primeiro elemento ou valor padrão se nenhum elemento do tipo for encontrado; assim para tipos por referência o valor padrão será um NULL para tipos por valor será um ZERO.

Podemos usar este método mesmo se houver mais de um resultado a ser retornado, ou seja, a condição vai ser atendida por mais de um elemento.

Esse método é útil quando você deseja recuperar apenas um elemento ou o primeiro elemento de uma coleção.

3- SingleOrDefaultAsync - Retorna de forma assíncrona o único elemento de uma sequência que satisfaça uma condição especificada ou um valor padrão se não existir esse elemento; Esse método gera uma exceção 'InvalidOperationException' se mais de um elemento satisfizer a condição.


  
 
var item1 = await _context.Categorias.SingleOrDefaultAsync(c => c.CategoriaId == id)   
      

Este método também vai retornar o primeiro elemento ou valor padrão se nenhum elemento do tipo for encontrado, mas se houver mais de resultado será gerado uma exceção.

Esse método é útil quando você espera que haja apenas um elemento correspondente e deseja garantir que não haja mais de um.

Conclusão

Dessa forma podemos definir a utilização destes métodos da seguinte forma:

1- O elemento a ser encontrado esta sendo rastreado pelo contexto do EF Core ?

Se o elemento estiver na memória então use FindAsync() pois ele vai buscar os dados na memória e não precisa ir no banco de dados.

Atente para o fato de que FindAsync() não suporta a cláusula Include.

2- Se o elemento a ser encontrado NÃO estiver na memória e você tem certeza de que vai existir somente um único resultado que vai atender o critério da consulta então use o método FirstOrDefaultAsync()

3- Se o elemento a ser encontrado NÃO estiver na memória e você NÃO tem certeza de que vai existir somente um único resultado que vai atender o critério da consulta então use o método SingleOrDefaultAsync()

E estamos conversados...

"Orem também para que sejamos libertos dos homens perversos e maus, pois a fé não é de todos.
Mas o Senhor é fiel; ele os fortalecerá e os guardará do Maligno."
2 Tessalonicenses 3:2,3

Referências:


José Carlos Macoratti