Muito já foi dito anteriormente, mas de volta às raízes, de uma maneira mais técnica:
IEnumerable
é uma coleção de objetos na memória que você pode enumerar - uma sequência na memória que possibilita a iteração (facilita o foreach
loop interno, embora você possa usar IEnumerator
apenas). Eles residem na memória como estão.
IQueryable
é uma árvore de expressão que será traduzida em outra coisa em algum momento com capacidade de enumerar o resultado final . Eu acho que é isso que confunde a maioria das pessoas.
Eles obviamente têm conotações diferentes.
IQueryable
representa uma árvore de expressão (uma consulta, simplesmente) que será traduzida para outra coisa pelo provedor de consulta subjacente assim que as APIs de liberação forem chamadas, como funções agregadas LINQ (Sum, Count, etc.) ou ToList [Array, Dictionary ,. ..] E os IQueryable
objetos também são implementados IEnumerable
, IEnumerable<T>
para que, se representarem uma consulta, o resultado dessa consulta possa ser iterado. Isso significa que o IQueryable não precisa ser apenas consultas. O termo certo é que são árvores de expressão .
Agora, como essas expressões são executadas e o que elas recorrem depende dos chamados provedores de consulta (executores de expressão em que podemos pensar).
No mundo do Entity Framework (que é o provedor de origem de dados subjacente místico, ou o provedor de consultas), as IQueryable
expressões são convertidas em consultas T-SQL nativas . Nhibernate
faz coisas semelhantes com eles. Você pode escrever o seu próprio seguindo os conceitos descritos em LINQ: Construindo um link IQueryable Provider , por exemplo, e você pode querer ter uma API de consulta personalizada para o serviço de provedor de loja de produtos.
Então, basicamente, os IQueryable
objetos estão sendo construídos até o momento em que os liberamos explicitamente e dizemos ao sistema para reescrevê-los no SQL ou qualquer outra coisa e enviar a cadeia de execução para processamento posterior.
Como para adiar a execução, é um LINQ
recurso para manter o esquema da árvore de expressão na memória e enviá-lo para a execução somente sob demanda, sempre que determinadas APIs são chamadas na sequência (a mesma contagem, lista de tarefas etc.).
O uso adequado de ambos depende muito das tarefas que você está enfrentando para o caso específico. Para o conhecido padrão de repositório, eu pessoalmente opto por retornar IList
, ou seja, IEnumerable
Listas (indexadores e outros). Portanto, é meu conselho usar IQueryable
somente dentro de repositórios e IEnumerable em qualquer outro lugar do código. Não estou dizendo sobre as preocupações de testabilidade que IQueryable
quebram e arruinam o princípio da separação de preocupações . Se você retornar uma expressão de dentro dos repositórios, os consumidores poderão brincar com a camada de persistência como desejariam.
Um pequeno acréscimo à bagunça :) (de uma discussão nos comentários)) Nenhum deles é um objeto na memória, pois não é um tipo real em si, é um tipo de marcador - se você quiser ir tão fundo. Mas faz sentido (e é por isso que até o MSDN as coloca dessa maneira) pensar em IEnumerables como coleções na memória, enquanto IQueryables como árvores de expressão. O ponto é que a interface IQueryable herda a interface IEnumerable, de modo que, se representa uma consulta, os resultados dessa consulta podem ser enumerados. A enumeração faz com que a árvore de expressão associada a um objeto IQueryable seja executada. Portanto, na verdade, você não pode realmente chamar nenhum membro IEnumerable sem ter o objeto na memória. Vai chegar lá se você, de qualquer maneira, se não estiver vazio. IQueryables são apenas consultas, não os dados.