Retornando IEnumerable vs. IQueryable

Qual é a diferença entre retornar IQueryable vs. IEnumerable ?

 IQueryable custs = from c in db.Customers where c.City == "" select c; IEnumerable custs = from c in db.Customers where c.City == "" select c; 

Ambos serão deferidos e quando um deve ser preferido ao outro?

Sim, ambos lhe darão a execução adiada .

A diferença é que IQueryable é a interface que permite que o LINQ para SQL (LINQ.-to-anything realmente) funcione. Portanto, se você refinar ainda mais sua consulta em um IQueryable , essa consulta será executada no database, se possível.

Para o caso IEnumerable , ele será LINQ-to-object, significando que todos os objects correspondentes à consulta original terão que ser carregados na memory do database.

Em código:

 IQueryable custs = ...; // Later on... var goldCustomers = custs.Where(c => c.IsGold); 

Esse código executará o SQL para selecionar apenas clientes de ouro. O código a seguir, por outro lado, executará a consulta original no database e, em seguida, filtrará os clientes que não são de ouro na memory:

 IEnumerable custs = ...; // Later on... var goldCustomers = custs.Where(c => c.IsGold); 

Essa é uma diferença bastante importante, e trabalhar em IQueryable pode, em muitos casos, evitar que você retorne muitas linhas do database. Outro excelente exemplo é fazer paginação: Se você usar Take and Skip em IQueryable , você só receberá o número de linhas solicitadas; fazer isso em um IEnumerable fará com que todas as linhas sejam carregadas na memory.

A resposta principal é boa, mas não menciona trees de expressão que explicam “como” as duas interfaces são diferentes. Basicamente, existem dois conjuntos idênticos de extensões LINQ. Where() , Sum() , Count() , FirstOrDefault() , etc, todos têm duas versões: uma que aceita funções e outra que aceita expressões.

  • A assinatura da versão IEnumerable é: Where(Func predicate)

  • A assinatura da versão IQueryable é: Where(Expression> predicate)

Você provavelmente já usou os dois sem perceber porque ambos são chamados usando uma syntax idêntica:

Por exemplo, Where(x => x.City == "") funciona em IEnumerable e IQueryable

  • Ao usar Where() em uma coleção IEnumerable , o compilador passa uma function compilada para Where()

  • Ao usar Where() em uma coleção IQueryable , o compilador passa uma tree de expressão para Where() . Uma tree de expressão é como o sistema de reflection, mas para código. O compilador converte seu código em uma estrutura de dados que descreve o que seu código faz em um formato que é facilmente digerível.

Por que se preocupar com essa coisa de tree de expressão? Eu só quero Where() para filtrar meus dados. A principal razão é que tanto os ORMs EF quanto os Linq2SQL podem converter trees de expressão diretamente em SQL, onde seu código será executado muito mais rápido.

Ah, isso soa como um aumento de desempenho livre, devo usar AsQueryable() todo o lugar nesse caso? Não, o IQueryable só é útil se o provedor de dados subjacente puder fazer algo com ele. Convertendo algo como uma List normal para IQueryable não lhe dará nenhum benefício.

Sim, ambos usam execução adiada. Vamos ilustrar a diferença usando o profiler do SQL Server ….

Quando nós executamos o seguinte código:

 MarketDevEntities db = new MarketDevEntities(); IEnumerable first = db.WebLogs; var second = first.Where(c => c.DurationSeconds > 10); var third = second.Where(c => c.WebLogID > 100); var result = third.Where(c => c.EmailAddress.Length > 11); Console.Write(result.First().UserName); 

No SQL Server profiler, encontramos um comando igual a:

 "SELECT * FROM [dbo].[WebLog]" 

Leva aproximadamente 90 segundos para executar esse bloco de código em uma tabela do WebLog que tenha 1 milhão de registros.

Portanto, todos os registros da tabela são carregados na memory como objects e, em seguida, com cada .Where () será outro filtro na memory contra esses objects.

Quando usamos IQueryable vez de IEnumerable no exemplo acima (segunda linha):

No SQL Server profiler, encontramos um comando igual a:

 "SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11" 

Leva aproximadamente quatro segundos para executar esse bloco de código usando o IQueryable .

IQueryable tem uma propriedade chamada Expression que armazena uma expressão de tree que começa a ser criada quando usamos o result em nosso exemplo (que é chamado de execução deferida) e, no final, essa expressão será convertida em uma consulta SQL para ser executada no mecanismo de database .

Ambos lhe darão a execução adiada, sim.

Quanto ao que é preferível ao outro, depende de qual é a sua origem de dados subjacente.

Retornar um IEnumerable forçará automaticamente o tempo de execução a usar o LINQ to Objects para consultar sua coleção.

Retornar um IQueryable (que implementa IEnumerable, por sinal) fornece a funcionalidade extra para traduzir sua consulta em algo que possa ter um melhor desempenho na fonte subjacente (LINQ para SQL, LINQ para XML, etc.).

Em geral, você deseja preservar o tipo estático original da consulta até que seja importante.

Por esse motivo, você pode definir sua variável como ‘var’ em vez de IQueryable<> ou IEnumerable<> e você saberá que não está alterando o tipo.

Se você começar com um IQueryable<> , normalmente desejará mantê-lo como um IQueryable<> até que haja algum motivo convincente para alterá-lo. A razão para isso é que você deseja fornecer ao processador de consultas o máximo de informações possível. Por exemplo, se você usar apenas 10 resultados (chamado Take(10) ), será necessário que o SQL Server saiba disso para otimizar seus planos de consulta e enviar apenas os dados que você usará. .

Uma razão convincente para alterar o tipo de IQueryable<> para IEnumerable<> pode ser que você esteja chamando alguma function de extensão que a implementação de IQueryable<> em seu object particular não pode manipular ou manipular de forma ineficiente. Nesse caso, talvez você queira converter o tipo para IEnumerable<> (atribuindo a uma variável do tipo IEnumerable<> ou usando o método de extensão AsEnumerable , por exemplo) para que as funções de extensão que você chama acabem sendo as do tipo Classe Queryable vez da class Queryable .

Em termos gerais, eu recomendaria o seguinte:

Para retornar IQueryable se você quiser habilitar o desenvolvedor usando seu método para refinar a consulta retornada antes de executar.

Se você quiser transportar apenas um conjunto de objects para enumerar, basta usar IEnumerable.

Imagine um IQueryable como o que é, uma “consulta” de dados (que você pode refinar se quiser)

Um IEnumerable é um conjunto de objects (que já foram recebidos ou criados) sobre os quais você pode enumerar.

Há uma postagem de blog com um breve exemplo de código-fonte sobre como o uso incorreto de IEnumerable pode afetar drasticamente o desempenho de consultas do LINQ: Entity Framework: IQueryable vs. IEnumerable .

Se nós cavarmos mais fundo e olharmos para as fonts, podemos ver que obviamente existem diferentes methods de extensão para o IEnumerable :

 // Type: System.Linq.Enumerable // Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 // Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll public static class Enumerable { public static IEnumerable Where( this IEnumerable source, Func predicate) { return (IEnumerable) new Enumerable.WhereEnumerableIterator(source, predicate); } } 

e IQueryable :

 // Type: System.Linq.Queryable // Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 // Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll public static class Queryable { public static IQueryable Where( this IQueryable source, Expression> predicate) { return source.Provider.CreateQuery( Expression.Call( null, ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod( new Type[] { typeof(TSource) }), new Expression[] { source.Expression, Expression.Quote(predicate) })); } } 

O primeiro retorna um iterador enumerável e o segundo cria consulta por meio do provedor de consulta, especificado na origem IQueryable .

Muito já foi dito anteriormente, mas de volta às raízes, de uma maneira mais técnica:

  1. IEnumerable é uma coleção de objects na memory que você pode enumerar – uma sequência na memory que possibilita a iteração (facilita muito o loop foreach , embora você possa usar o IEnumerator apenas). Eles residem na memory como estão.
  2. IQueryable é uma tree de expressão que será traduzida em outra coisa em algum momento com capacidade de enumerar o resultado final . Eu acho que isso é o que confunde a maioria das pessoas.

Eles obviamente têm conotações diferentes.

IQueryable representa uma tree de expressão (uma consulta, simplesmente) que será traduzida para outra coisa pelo provedor de consulta subjacente assim que as APIs de lançamento forem chamadas, como funções agregadas LINQ (sum, contagem, etc.) ou ToList [Array, Dictionary, …]. E objects IQueryable também implementam IEnumerable , IEnumerable para que, se eles representarem uma consulta, o resultado dessa consulta possa ser iterado. Isso significa que o IQueryable não precisa ser apenas de consultas. O termo certo é que eles são trees de expressão .

Agora, como essas expressões são executadas e a que elas recorrem, tudo depende dos chamados provedores de consulta (executores de expressões que podemos pensar neles).

No mundo do Entity Framework (que é esse provedor de fonte de dados subjacente místico ou o provedor de consulta), as expressões IQueryable são convertidas em consultas T-SQL nativas. Nhibernate faz coisas semelhantes com eles. Você pode escrever o seu próprio seguindo os conceitos bem descritos no LINQ: Criando um link do IQueryable Provider , por exemplo, e você pode querer ter uma API de consulta customizada para o seu serviço de provedor de armazenamento de produto.

Então, basicamente, os objects IQueryable estão sendo construídos durante todo o tempo até que os liberemos explicitamente e digamos ao sistema para reescrevê-los em SQL ou qualquer outra coisa e enviar a cadeia de execução para processamento posterior.

Como se fosse uma execução adiada , é um recurso LINQ para manter o esquema de tree de expressão na memory e enviá-lo para a execução apenas sob demanda, sempre que certas APIs forem chamadas contra a sequência (a mesma Count, ToList, etc.).

O uso adequado de ambos depende muito das tarefas que você está enfrentando para o caso específico. Para o padrão de repository conhecido, eu pessoalmente opto por retornar IList , isto é, IEnumerable over Lists (indexadores e similares). Portanto, é meu conselho usar o IQueryable somente dentro de repositorys e IEnumerable em qualquer outro lugar no código. Não estou dizendo sobre as preocupações de testabilidade que IQueryable quebra e arruína o princípio da separação de interesses . Se você retornar uma expressão de dentro de repositorys, os consumidores poderão brincar com a camada de persistência como desejariam.

Um pequeno acréscimo à bagunça 🙂 (a partir de uma discussão nos comentários)) Nenhum deles são objects na memory, já que eles não são tipos reais em si, são marcadores de um tipo – se você quiser ir tão fundo. Mas faz sentido (e é por isso que mesmo o MSDN coloca dessa maneira) pensar em IEnumerables como collections na memory enquanto IQueryables como trees de expressão. O ponto é que a interface IQueryable herda a interface IEnumerable para que, se ela representar uma consulta, os resultados dessa consulta possam ser enumerados. Enumeração faz com que a tree de expressão associada a um object IQueryable seja executada. Portanto, na verdade, você não pode realmente chamar nenhum membro IEnumerable sem ter o object na memory. Ele entrará lá se você fizer, de qualquer maneira, se não estiver vazio. IQueryables são apenas consultas, não os dados.

Recentemente, tive um problema com o IEnumrable v. IQueryable. O algoritmo usado primeiro executou uma consulta IQueryable para obter um conjunto de resultados. Estes foram então passados ​​para um loop foreach, com os itens instanciados como uma class EF. Essa class EF foi então usada na cláusula from de uma consulta Linq to Entity, fazendo com que o resultado fosse IEnumerable. Sou relativamente novo na EF e no Linq for Entities, então demorou um pouco para descobrir qual era o gargalo. Usando MiniProfiling, encontrei a consulta e, em seguida, converti todas as operações individuais em uma única consulta IQQueryable Linq for Entities. O IEnumerable levou 15 segundos e o IQueryable levou 0,5 segundos para ser executado. Havia três tabelas envolvidas e, depois de ler isso, acredito que a consulta IEnumerable estava realmente formando um produto cruzado de três tabelas e filtrando os resultados.

Tente usar o IQueryables como regra geral e crie um perfil de seu trabalho para tornar suas alterações mensuráveis.

Eu gostaria de esclarecer algumas coisas devido a respostas aparentemente conflitantes (principalmente ao redor de IEnumerable).

(1) O IQueryable estende a interface IEnumerable . (Você pode enviar um IQueryable para algo que espera IEnumerable sem erro.)

(2) Tanto IQueryable quanto IEnumerable LINQ tentam carregamento lento ao iterar sobre o conjunto de resultados. (Observe que a implementação pode ser vista em methods de extensão de interface para cada tipo.)

Em outras palavras, IEnumerables não são exclusivamente “in-memory”. IQueryables nem sempre são executados no database. IEnumerable deve carregar coisas na memory (uma vez recuperadas, possivelmente preguiçosamente) porque não possui um provedor de dados abstrato. IQueryables conta com um provedor abstrato (como o LINQ para SQL), embora esse também possa ser o provedor de memory .NET.

Exemplo de uso de amostra

(a) Recuperar lista de registros como IQueryable do contexto EF. (Nenhum registro está na memory.)

(b) Passe o IQueryable para uma exibição cujo modelo seja IEnumerable . (Válido. IQueryable estende IEnumerable .)

(c) Iterar e acessar os registros, entidades filhas e propriedades do dataset da exibição. (Pode causar exceções!)

Problemas possíveis

(1) O IEnumerable tenta carregamento lento e seu contexto de dados é expirado. Exceção lançada porque o provedor não está mais disponível.

(2) Os proxies de entidade do Entity Framework são habilitados (o padrão) e você tenta acessar um object relacionado (virtual) com um contexto de dados expirado. O mesmo que (1).

(3) Múltiplos Conjuntos de Resultados Ativos (MARS). Se você estiver iterando sobre o IEnumerable em um bloco foreach( var record in resultSet ) e simultaneamente tentar acessar record.childEntity.childProperty , poderá acabar com o MARS devido ao carregamento lento do dataset e da entidade relacional. Isso causará uma exceção se não estiver ativado em sua cadeia de conexão.

Solução

  • Descobri que ativar o MARS na cadeia de conexão funciona de maneira não confiável. Eu sugiro que você evite MARS a menos que seja bem compreendido e explicitamente desejado.

Execute a consulta e armazene os resultados invocando resultList = resultSet.ToList() Essa parece ser a maneira mais direta de garantir que suas entidades estejam na memory.

Nos casos em que você está acessando entidades relacionadas, você ainda pode exigir um contexto de dados. Ou isso, ou você pode desabilitar proxies de entidade e Include explicitamente entidades relacionadas a partir do seu DbSet .

A principal diferença entre “IEnumerable” e “IQueryable” é sobre onde a lógica do filtro é executada. Um é executado no lado do cliente (na memory) e o outro é executado no database.

Por exemplo, podemos considerar um exemplo em que temos 10.000 registros para um usuário em nosso database e digamos que apenas 900 são usuários ativos, portanto, neste caso, se usar “IEnumerable”, primeiro carregamos todos os 10.000 registros na memory e Em seguida, aplica o filtro IsActive nele que, eventualmente, retorna os 900 usuários ativos.

Enquanto, por outro lado, no mesmo caso, se usarmos “IQueryable”, aplicará diretamente o filtro IsActive no database, o qual, diretamente a partir dele, retornará os 900 usuários ativos.

Link de Referência

estas são algumas diferenças entre IQueryable e IEnumerable

diferença entre retornar IQueryable <T/> vs. IEnumerable <t>“> </t></p>
</div>
</li><!-- #comment-## -->
<div class=

Podemos usar os dois da mesma maneira, e eles são diferentes no desempenho.

O IQueryable só executa contra o database de maneira eficiente. Isso significa que ele cria uma consulta de seleção inteira e obtém apenas os registros relacionados.

Por exemplo, queremos include os 10 principais clientes cujo nome começa com “Nimal”. Nesse caso, a consulta de seleção será gerada como select top 10 * from Customer where name like 'Nimal%' .

Mas se usássemos IEnumerable, a consulta seria como select * from Customer where name like 'Nimal%' e os dez primeiros serão filtrados no nível de codificação C # (obtém todos os registros de clientes do database e os transmite para C #) .

Além das 2 primeiras respostas realmente boas (por driis & by Jacob):

Interface IEnumerable está no namespace System.Collections.

O object IEnumerable representa um dataset na memory e pode mover somente esses dados para frente. A consulta representada pelo object IEnumerable é executada imediatamente e completamente, portanto, o aplicativo recebe dados rapidamente.

Quando a consulta é executada, IEnumerable carrega todos os dados e, se precisarmos filtrá-los, a própria filtragem é feita no lado do cliente.

A interface IQueryable está localizada no namespace System.Linq.

O object IQueryable fornece access remoto ao database e permite que você navegue pelos dados em ordem direta, do início ao fim, ou na ordem inversa. No processo de criação de uma consulta, o object retornado é IQueryable, a consulta é otimizada. Como resultado, menos memory é consumida durante sua execução, menos largura de banda de rede, mas ao mesmo tempo pode ser processada um pouco mais lentamente que uma consulta que retorna um object IEnumerable.

O que escolher?

Se você precisar de todo o dataset retornados, é melhor usar IEnumerable, que fornece a velocidade máxima.

Se você NÃO precisa de todo o dataset retornados, mas apenas alguns dados filtrados, é melhor usar o IQueryable.

Ao usar o LINQ to Entities, é importante entender quando usar IEnumerable e IQueryable. Se usarmos IEnumerable, a consulta será executada imediatamente. Se usarmos IQueryable, a execução da consulta será adiada até que o aplicativo solicite a enumeração. Agora vamos ver o que deve ser considerado ao decidir usar IQueryable ou IEnumerable. Usar o IQueryable oferece a você a chance de criar uma consulta LINQ complexa usando várias instruções sem executar a consulta no nível do database. A consulta é executada somente quando a consulta final do LINQ é enumerada.