Pergunta

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 queira realizar alguma ação no select que não pode ser convertida para SQL.Entendo que a maneira convencional de conseguir isso é fazer AsEnumerable() convertendo-o assim em um objeto viável.

Dado este código atualizado:

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

E SQL atualizado:

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" está armazenada em cache na memória?Esse desempenho seria lento se a tabela contivesse uma grande quantidade de registros?

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

Foi útil?

Solução

A razão para AsEnumerable é para

Atenumerable (Tsource) (ienumerable (tsource)) pode ser usado para escolher entre implementações de consulta quando uma sequência implementa ienumerable (t), mas também possui um conjunto diferente de métodos de consulta pública disponíveis disponíveis

Então, quando você estava ligando para o Where método antes, você estava chamando um método diferente Where método do IEnumerable.Where.Que Where declaração era para o LINQ converter para SQL, o novo Where é o IEnumerable aquele que leva um IEnumerable, enumera-o e produz os itens correspondentes.O que explica por que você vê diferentes SQL sendo gerados.A tabela será retirada integralmente do banco de dados antes do Where extensão será aplicada em sua segunda versão do código.Isso poderia criar um sério gargalo, porque toda a tabela teria que estar na memória, ou pior, toda a tabela teria que viajar entre servidores.Permitir que o SQL Server execute o Where e faça o que faz de melhor.

Outras dicas

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

Uma solução parcial pode ser o caminho.Considerar

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

Digamos também que NonDatabaseConvertableCriterion requer o campo C do resultado.Como 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}
);

Neste caso, quando res é enumerado, consultado ou utilizado de outra forma, tanto trabalho quanto possível será passado para o banco de dados, que retornará o suficiente para continuar o trabalho.Supondo que seja realmente impossível reescrever para que todo o trabalho possa ser enviado para o banco de dados, este pode ser um compromisso adequado.

Existem três implementações de AsEnumerable.

DataTableExtensions.AsEnumerable

Estende um DataTable para dar-lhe um IEnumerable interface para que você possa usar o Linq contra o DataTable.

Enumerable.AsEnumerable<TSource> e ParallelEnumerable.AsEnumerable<TSource>

O AsEnumerable<TSource>(IEnumerable<TSource>) O método não tem efeito além de alterar o tipo de fonte do tempo de compilação de um tipo que implementa IEnumerable<T> para IEnumerable<T> em si.

AsEnumerable<TSource>(IEnumerable<TSource>) pode ser usado para escolher entre implementações de consulta quando uma sequência implementa IEnumerable<T> mas também possui um conjunto diferente de métodos de consulta pública disponíveis.Por exemplo, dada uma classe genérica Table que implementa IEnumerable<T> e tem seus próprios métodos, como Where, Select, e SelectMany, uma chamada para Where invocaria o público Where método de Table.A Table tipo que representa uma tabela de banco de dados pode ter um Where Método que toma o argumento do predicado como uma árvore de expressão e converte a árvore em SQL para execução remota.Se a execução remota não for desejada, por exemplo, porque o predicado invoca um método local, o AsEnumerable<TSource> O método pode ser usado para ocultar os métodos personalizados e, em vez disso, disponibilizar os operadores de consulta padrão.

Em outras palavras.

Se eu tiver um

IQueryable<X> sequence = ...;

de um LinqProvider, como Entity Framework, e eu,

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

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

Se eu quiser executar a instrução com Linq to Objects, eu faço,

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

agora o servidor retornará todos os dados e o Enumerable.Where do Linq para Objects será usado em vez da implementação do Query Provider.

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

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top