Existem muitos tópicos sobre stackoverflow: Quando devo usar IEnumerable e quando IQueryable? Qual é a diferença entre IQueryable<T> e IEnumerable<T>? Quando devo usar IEnumerable e quando IQueryable? Qual é a diferença entre IQueryable e IEnumerable
Essas postagens afirmam que IEnumerable
filtra os dados da memória enquanto IQueryable
o faz no lado do servidor. Mas descobri que isso é impreciso.
IEnumerable
filtra dados da memória somente se dividirmos uma consulta!
Por exemplo, se eu usar IEnumerable
assim
IEnumerable<Customer> customers = context.Customers.Where(c => c.FirstName.StartsWith("J")).Take(5).ToList();
fornece uma consulta correta com o filtro top(5):
select top(5) * from Customers WHERE FirstName like 'j%';
Isso significa que Where
e Take
são codificados na consulta e executados no servidor, o que contradiz o que afirmam as postagens acima.
Por outro lado, se eu dividir a consulta:
IEnumerable<Customer> customers = context.Customers.Where(c => c.FirstName.StartsWith("J"));
var result = customers.Take(5).ToList(); //the filter is in a different line of code
dá uma consulta sem top(5):
select * from Customers WHERE FirstName like 'j%';
o que significa que a Take(5)
parte é executada na memória.
Por que as pessoas ainda afirmam que isso IEnumerable
filtra os dados da memória enquanto IQueryable
o faz no lado do servidor?
Quando dizemos "
IEnumerable
faz isso e aquilo" ou "IQueryable
faz isso e aquilo", não estamos falando sobre o tipo da variável à qual você está atribuindo o resultado.Estamos falando sobre o conjunto de operadores LINQ que estamos usando.
Enumerable
declara operadores LINQ que operam emIEnumerable<T>
, enquantoQueryable
declara operadores LINQ que operam emIQueryable<T>
. Os primeiros operadores operam na memória, enquanto os últimos operadores seriam traduzidos em SQL e executados no banco de dados.No primeiro exemplo, onde você não "dividiu" a consulta,
context.Customers
é umIQueryable
(presumivelmente). OsWhere
,Take
eToList
que você chama são todos declarados emQueryable
. O fato de você ter declaradocustomers
ser do tipoIEnumerable<Customer>
é irrelevante. Na verdade, você não está "usando"IEnumerable
aqui - você não está chamando os métodos declarados emEnumerable
.No segundo exemplo,
Take
não é chamado em um arquivoIQueryable
. É chamadocustomers
, do tipoIEnumerable<Customer>
, como você declarou. Como resultado, você acaba chamandoTake
o que está declarado emEnumerable
, então aTake
parte não é traduzida para SQL.Observe que os operadores LINQ são todos métodos de extensão, portanto não há despacho dinâmico - que
Take
é chamado (Enumerable.Take
ouQueryable.Take
) depende do tipo de tempo de compilação da expressão na qual você o chamou. O fato decustomers
realmente armazenar uma instância de algum tipo que implementaIQueryable<T>
é irrelevante.