Conheço algumas diferenças de LINQ to Entities e LINQ to Objects que o primeiro implementa IQueryable
e o segundo implementa IEnumerable
e o escopo da minha pergunta está no EF 5.
Minha pergunta é: qual é a diferença técnica desses 3 métodos? Eu vejo que em muitas situações todos eles funcionam. Eu também vejo usando combinações deles como .ToList().AsQueryable()
.
O que esses métodos significam exatamente?
Existe algum problema de desempenho ou algo que levaria ao uso de um sobre o outro?
Por que alguém usaria, por exemplo, em
.ToList().AsQueryable()
vez de.AsQueryable()
?
Respostas:
Há muito a dizer sobre isso. Deixe-me focar
AsEnumerable
eAsQueryable
e mençãoToList()
ao longo do caminho.O que esses métodos fazem?
AsEnumerable
eAsQueryable
converter ou converter paraIEnumerable
ouIQueryable
, respectivamente. Digo elenco ou converter com uma razão:Quando o objeto de origem já implementa a interface de destino, o próprio objeto de origem é retornado, mas convertido na interface de destino. Em outras palavras: o tipo não é alterado, mas o tipo em tempo de compilação é.
Quando o objeto de origem não implementa a interface de destino, o objeto de origem é convertido em um objeto que implementa a interface de destino. Portanto, o tipo e o tipo de tempo de compilação são alterados.
Deixe-me mostrar isso com alguns exemplos. Eu tenho esse pequeno método que relata o tipo de tempo de compilação e o tipo real de um objeto ( cortesia de Jon Skeet ):
Vamos tentar um linq-to-sql arbitrário
Table<T>
, que implementaIQueryable
:O resultado:
Você vê que a própria classe da tabela sempre é retornada, mas sua representação é alterada.
Agora, um objeto que implementa
IEnumerable
, nãoIQueryable
:Os resultados:
Aí está.
AsQueryable()
converteu a matriz em umEnumerableQuery
, que "representa umaIEnumerable<T>
coleção como umaIQueryable<T>
fonte de dados". (MSDN).Qual o uso?
AsEnumerable
é freqüentemente usado para alternar de qualquerIQueryable
implementação para LINQ para objetos (L2O), principalmente porque o primeiro não suporta funções que o L2O possui. Para obter mais detalhes, consulte Qual é o efeito de AsEnumerable () em uma entidade LINQ? .Por exemplo, em uma consulta do Entity Framework, podemos usar apenas um número restrito de métodos. Portanto, se, por exemplo, precisarmos usar um de nossos próprios métodos em uma consulta, normalmente escreveremos algo como
ToList
- que converte umIEnumerable<T>
emList<T>
- é também frequentemente usado para esse fim. A vantagem de usarAsEnumerable
vs.ToList
é queAsEnumerable
não executa a consulta.AsEnumerable
preserva a execução adiada e não cria uma lista intermediária geralmente inútil.Por outro lado, quando a execução forçada de uma consulta LINQ é desejada,
ToList
pode ser uma maneira de fazer isso.AsQueryable
pode ser usado para fazer uma coleção enumerável aceitar expressões em instruções LINQ. Veja aqui para mais detalhes: Eu realmente preciso usar AsQueryable () na coleção? .Nota sobre abuso de substâncias!
AsEnumerable
funciona como uma droga. É uma solução rápida, mas com um custo e não soluciona o problema subjacente.Em muitas respostas de estouro de pilha, vejo pessoas se candidatando
AsEnumerable
para corrigir praticamente qualquer problema com métodos não suportados nas expressões LINQ. Mas o preço nem sempre é claro. Por exemplo, se você fizer isso:... tudo está perfeitamente traduzido em uma instrução SQL que filtra (
Where
) e projeta (Select
). Ou seja, o comprimento e a largura, respectivamente, do conjunto de resultados SQL são reduzidos.Agora, suponha que os usuários desejem apenas ver a parte da data
CreateDate
. No Entity Framework, você descobrirá rapidamente que ...... não é suportado (no momento da escrita). Felizmente, há a
AsEnumerable
solução:Claro, corre, provavelmente. Mas ele puxa a tabela inteira para a memória e depois aplica o filtro e as projeções. Bem, a maioria das pessoas é inteligente o suficiente para fazer o
Where
primeiro:Mas ainda todas as colunas são buscadas primeiro e a projeção é feita na memória.
A correção real é:
(Mas isso requer um pouco mais de conhecimento ...)
O que esses métodos NÃO fazem?
Restaurar recursos IQueryable
Agora, uma ressalva importante. Quando você faz
você terminará com o objeto de origem representado como
IQueryable
. (Porque os dois métodos apenas convertem e não convertem).Mas quando você faz
qual será o resultado?
O
Select
produz umWhereSelectEnumerableIterator
. Esta é uma classe .Net interna que implementaIEnumerable
, nãoIQueryable
. Portanto, ocorreu uma conversão para outro tipo e o subsequenteAsQueryable
não pode mais retornar a fonte original.A implicação disso é que o uso não
AsQueryable
é uma maneira de injetar magicamente um provedor de consulta com seus recursos específicos em um enumerável. Suponha que você façaA condição where nunca será convertida em SQL.
AsEnumerable()
seguido por instruções LINQ corta definitivamente a conexão com o provedor de consulta de estrutura de entidade.Eu deliberadamente mostro este exemplo, porque já vi perguntas aqui onde pessoas, por exemplo, tentam 'injetar'
Include
recursos em uma coleção chamandoAsQueryable
. Ele compila e executa, mas não faz nada porque o objeto subjacente não possui mais umaInclude
implementação.Executar
Ambos
AsQueryable
eAsEnumerable
não executam (ou enumeram ) o objeto de origem. Eles apenas alteram seu tipo ou representação. Ambas as interfaces envolvidasIQueryable
eIEnumerable
são nada mais que "uma enumeração esperando para acontecer". Eles não são executados antes de serem forçados a fazê-lo, por exemplo, como mencionado acima, chamandoToList()
.Isso significa que executar um
IEnumerable
obtido chamandoAsEnumerable
umIQueryable
objeto executará o subjacenteIQueryable
. Uma execução subsequente doIEnumerable
executará novamente oIQueryable
. O que pode ser muito caro.Implementações específicas
Até agora, isso era apenas sobre os métodos de extensão
Queryable.AsQueryable
eEnumerable.AsEnumerable
. Mas é claro que qualquer um pode escrever métodos de instância ou métodos de extensão com os mesmos nomes (e funções).De fato, é um exemplo comum de um
AsEnumerable
método de extensão específicoDataTableExtensions.AsEnumerable
.DataTable
não implementaIQueryable
ouIEnumerable
, portanto, os métodos de extensão regulares não se aplicam.fonte
AsQueryable()
geralmente se baseia em conceitos errôneos. Mas vou deixar ferver na parte de trás da minha cabeça por um tempo e ver se posso adicionar mais cobertura sobre essa questão.Listar()
AsEnumerable ()
Func<TSource, bool>
AsQueryable ()
Expression<Func<TSource, bool>>
AsQueryable()
geralmente funciona muito mais rápido do queAsEnumerable()
quando gera o T-SQL no início, o que inclui todas as suas condições where em seu Linq.fonte
ToList () será tudo na memória e você estará trabalhando nisso. portanto, ToList (). where (aplicar algum filtro) é executado localmente. O AsQueryable () executará tudo remotamente, ou seja, um filtro será enviado ao banco de dados para aplicação. Queryable não faz nada até você executá-lo. ToList, no entanto, é executado imediatamente.
Além disso, veja esta resposta Por que usar AsQueryable () em vez de List ()? .
EDIT: Além disso, no seu caso, depois de fazer ToList (), todas as operações subseqüentes são locais, incluindo AsQueryable (). Você não pode mudar para remoto depois de começar a executar localmente. Espero que isso torne um pouco mais claro.
fonte
Foi encontrado um desempenho ruim no código abaixo.
Corrigido com
Para um IQueryable, permaneça no IQueryable quando possível, tente não ser usado como IEnumerable.
Update . Pode ser ainda mais simplificado em uma expressão, graças a Gert Arnold .
fonte