Noções básicas sobre .AsEnumerable () no LINQ to SQL

Dada a seguinte consulta LINQ to SQL:

var test = from i in Imports where i.IsActive select i; 

A instrução SQL interpretada é:

 SELECT [t0].[id] AS [Id] .... FROM [Imports] AS [t0] WHERE [t0].[isActive] = 1 

Digamos que eu quisesse executar alguma ação na seleção que não pode ser convertida em SQL. É meu entendimento que a maneira convencional de realizar isso é fazer AsEnumerable() convertendo-o assim em um object viável.

Dado este código atualizado:

 var test = from i in Imports.AsEnumerable() where i.IsActive select new { // Make some method call }; 

E atualizado SQL:

 SELECT [t0].[id] AS [Id] ... FROM [Imports] AS [t0] 

Observe a falta de uma cláusula where na instrução SQL executada.

Isso significa que toda a tabela “Importações” é armazenada em cache na memory? Esse desempenho seria lento se a tabela continha uma grande quantidade de registros?

Ajude-me a entender o que está realmente acontecendo nos bastidores aqui.

A razão para AsEnumerable é

AsEnumerable (TSource) (IEnumerable (TSource)) pode ser usado para escolher entre implementações de consulta quando uma seqüência implementa IEnumerable (T), mas também tem um conjunto diferente de methods de consulta pública disponíveis

Então, quando você estava chamando o método Where antes, você estava chamando um método Where diferente do IEnumerable.Where. A declaração Where Where era para o LINQ converter para SQL, o novo Where é o IEnumerable que usa um IEnumerable, enumera e produz os itens correspondentes. O que explica porque você vê o SQL diferente sendo gerado. A tabela será retirada integralmente do database antes da extensão Where ser aplicada na segunda versão do código. Isso poderia criar um gargalo sério, porque a tabela inteira tem que estar na memory, ou pior, a tabela inteira teria que viajar entre os servidores. Permitir que o SQL Server execute o Where e faça o que faz melhor.

No ponto em que a enumeração é enumerada, o database será consultado e o conjunto de resultados inteiro será recuperado.

Uma solução de parte e parte pode ser o caminho. Considerar

 var res = ( from result in SomeSource where DatabaseConvertableCriterion(result) && NonDatabaseConvertableCriterion(result) select new {result.A, result.B} ); 

Vamos dizer também que NonDatabaseConvertableCriterion requer o campo C do resultado. Porque NonDatabaseConvertableCriterion faz o que seu nome sugere, isso deve ser executado como uma enumeração. No entanto, considere:

 var partWay = ( from result in SomeSource where DatabaseConvertableCriterion(result) select new {result.A, result.B, result.C} ); var res = ( from result in partWay.AsEnumerable() where NonDatabaseConvertableCriterion select new {result.A, result.B} ); 

Nesse caso, quando res for enumerado, consultado ou usado de outra forma, o máximo de trabalho possível será passado para o database, que retornará o suficiente para continuar o trabalho. Supondo que seja realmente impossível reescrevê-lo para que todo o trabalho possa ser enviado para o database, isso pode ser um compromisso adequado.

Existem três implementações de AsEnumerable .

DataTableExtensions.AsEnumerable

Estende um DataTable para fornecer uma interface IEnumerable para que você possa usar o Linq no DataTable .

Enumerable.AsEnumerable e ParallelEnumerable.AsEnumerable

O AsEnumerable(IEnumerable) não tem nenhum efeito além de alterar o tipo de fonte em tempo de compilation de um tipo que implementa IEnumerable para IEnumerable si.

AsEnumerable(IEnumerable) pode ser usado para escolher entre implementações de consulta quando uma seqüência implementa IEnumerable mas também tem um conjunto diferente de methods de consulta pública disponíveis. Por exemplo, dada uma class genérica Table que implementa IEnumerable e possui seus próprios methods como Where , Select e SelectMany , uma chamada para Where invocaria o método Where public. Um tipo de Table que representa uma tabela de database pode ter um método Where que usa o argumento do predicado como uma tree de expressão e converte a tree em SQL para execução remota. Se a execução remota não for desejada, por exemplo, porque o predicado chama um método local, o método AsEnumerable pode ser usado para ocultar os methods customizados e, em vez disso, disponibilizar os operadores de consulta padrão.

Em outras palavras.

Se eu tiver um

 IQueryable sequence = ...; 

de um LinqProvider, como Entity Framework, e eu faço,

 sequence.Where(x => SomeUnusualPredicate(x)); 

essa consulta será composta e executada no servidor. Isso falhará no tempo de execução porque o EntityFramework não sabe como converter SomeUnusualPredicate em SQL.

Se eu quero que execute a instrução com o Linq para objects em vez disso, eu faço,

 sequence.AsEnumerable().Where(x => SomeUnusualPredicate(x)); 

agora o servidor retornará todos os dados e o Enumerable.Where from Linq to Objects será usado no lugar da implementação do Provedor de Consultas.

Não importa que o Entity Framework não saiba como interpretar o SomeUnusualPredicate , minha function será usada diretamente. (No entanto, isso pode ser uma abordagem ineficiente, já que todas as linhas serão retornadas do servidor.)

Eu acredito que o AsEnumerable apenas informa ao compilador quais methods de extensão usar (neste caso, os definidos para IEnumerable em vez daqueles para IQueryable). A execução da consulta ainda é adiada até você chamar ToArray ou enumerar nela.