LINQ
-
Entendendo Deferred Execution ou execução adiada
![]() |
No artigo de hoje eu vou abordar os conceitos sobre Deferred Execution em consultas LINQ, explicando como esse recurso funciona, e porque você deve entender bem esse conceito para poder usar o recurso de forma adequada em suas aplicações. |
Vamos começar com a tradução de Deferred Execution que em português seria
algo como execução adiada, postergada, retardada, etc.
O importante é que fique claro o entendimento de que o termo significa uma execução que não foi realizada no momento e foi adiada para ser executada posteriormente.
Quando usamos consultas LINQ (Language Integrated Query), elas podem possuir dois comportamentos de execução distintos os quais são:
|
LINQ -
Language integrated Query - é um conjunto de recursos introduzidos
no .NET Framework 3.5 que permitem a realização de consultas diretamente em base de dados, documentos XML , estrutura de dados , coleção de objetos ,etc. usando uma sintaxe parecida com a linguagem SQL. |
Execução Deferred ou Adiada
Execução Imediata
Quando o LINQ executa uma consulta ele utiliza a execução adiada ou Deferred Execution, e, isso significa que a consulta real não é executada até que os dados sejam realmente requisitados e acessados pela iteração e não quando a consulta é criada. Esse é o comportamento padrão do LINQ.
Obs: Em outras palavras : "Uma consulta LINQ que contém somente
métodos com execução adiada, não será executada até que os itens no resultado
sejam enumerados"
|
Para este exemplo
vamos criar uma aplicação do tipo Console Application usando o o
VS 2013
Express for windows desktop. Vamos criar uma aplicação usando a linguagem C# e outra usando a linguagem VB .NET. |
Como assim ?
Vamos explicar usando um exemplo de uma consulta LINQ bem simples. Considere o código abaixo:
Execução Adiada ou Deferred:
|
int[] numeros = { 1, 2, 3, 4, 5 ,6 ,7};
int i = 3;
var resultado = from n in numeros
foreach (var n in resultado) Console.ReadKey();
|
Dim numeros As Integer() = {1, 2, 3, 4, 5, 6, 7}
Dim i As Integer = 3
Dim resultado = From n In numeros Where n <= i Select n For Each n In resultado Console.WriteLine(n) Next Console.ReadKey()
|
A consulta LINQ usada neste exemplo é a seguinte:
|
var resultado = from n in numeros
|
Dim resultado = From n In numeros
Where n <= i
Select n
|
Você poderia supor que este código executa a consulta LINQ. Mas não é isso o que
ocorre. Este código apenas capta a ideia da consulta em uma estrutura de dados
chamada uma árvore de expressão ou expression tree.
A árvore contém informações sobre a classe/tabela/lista que você deseja
consultar, a consulta que você quer fazer e o resultado que você deseja
retornar. Porém a consulta não é executada.
Vamos analisar a estrutura da consulta em mais detalhes:
O operador Where é usado para filtrar os resultados de uma pesquisa baseada em uma condição especificada. (Where n <= i)
No código acima, o operador Where instrui a consulta para recuperar somente os valores que são inferiores ou iguais a qualquer que seja o valor de i.
No código exemplo atribuímos o valor 3 à variável i. ( i=3 )
Portanto, quando a expressão de consulta for executada, o operador Where vai comparar cada número da lista com 3 verificando se ele é menor ou igual a ele.
Em seguida, os números que atenderem a condição do operador Where serão incluídos no resultado.
Você deve estar assumindo agora que a variável resultado contém valores de 1, 2 e 3, mas, na verdade, ele contém apenas um cálculo de como você pode obter esses valores.
Quando realizamos a iteração na variável resultado usando um laço foreach/For Each é que a consulta é executada e o resultado é exibido:
No nosso exemplo a consulta é executada quando o laço foreach/For Each é executado:
|
foreach (var n in
resultado) <== (Executa Aqui) |
For Each n In resultado <== (Executa Aqui)
Console.WriteLine(n)
Next
|
O resultado obtido é mostrado abaixo:

Bem , você pode não estar convencido de que tudo isso que foi dito é verdade, pois o resultado é o esperado.
Então como podemos provar que a consulta é executada somente quando o laço foreach/For Each é executado ?
Elementar meu caro, vamos alterar o código atribuindo um novo valor à variável i após a consulta ser definida e antes do laço foreach/For Each ser executado. Veja abaixo como ficou o código:
Execução Adiada:
|
int[] numeros = { 1, 2, 3, 4, 5 ,6 ,7};
int i = 3;
var resultado = from n in numeros i=4;
foreach (var n in resultado) Console.ReadKey();
|
Dim numeros As Integer() = {1, 2, 3, 4, 5, 6, 7}
Dim i As Integer = 3
Dim resultado = From n In numeros Where n <= i Select n i=4 For Each n In resultado Console.WriteLine(n) Next Console.ReadKey()
|
Executando o código novamente iremos obter o seguinte resultado:

Veja que o resultado mudou, logo o fato de eu alterar o valor da variável i, mesmo fazendo isso depois da consulta, alterou o resultado da consulta. Isso prova que a execução foi adiada.
Alteramos o valor da variável i para 4.
E isso afeta diretamente o resultado da expressão da consulta por causa da execução adiada ou Deferred Execution.
Quando modificamos o valor de i, a expressão da consulta foi atualizada e o resultado apresenta os valores de 1 a 4 e não de 1 a 3;
Por causa da Deferred Execution o programa tem permissão para atualizar a expressão da consulta antes de executar para obter os valores.
A Execução adiada é o comportamento padrão do LINQ, mas você também pode fazer com que a consulta tenha a execução imediata.
Para fazer isso você pode usar um outro conjunto de métodos do namespace System.Linq que é composto por ToArray<T>(), ToList<T>(), ToDictionary<T>(), e ToLookup<T>(), Count, etc.
|
Nota : Na relação abaixo temos os métodos de extensão usados na LINQ que possuem execução adiada (deferred)
|
Existem também os métodos de extensão para a interface IEnumerable<T>.
Por exemplo, você pode usar o método ToList<T>() para converter o
resultado da expressão da consulta em uma coleção List<T>.
Veja no código abaixo como podemos usar ToList() para executar
imediatamente a expressão de consulta:
Execução Imediata:
|
int[] numeros = { 1, 2, 3, 4, 5 ,6 ,7};
int i = 3;
var
resultado = (from n in numeros i=4;
foreach (var n in resultado) Console.ReadKey();
|
Dim numeros As Integer() = {1, 2, 3, 4, 5, 6, 7}
Dim i As Integer = 3
Dim resultado = (From n In numeros Where n <= i Select n).ToList() i=4 For Each n In resultado Console.WriteLine(n) Next Console.ReadKey()
|
Agora executando o código acima teremos:

Agora com a alteração feita, a expressão da consulta foi executada imediatamente
quando o método ToList() foi chamado, e, dessa forma, a variável
resultado já contém os valores finais da consulta.
Isso pode ser confirmado pelo fato de que mesmo tendo alterado o valor da
variável i após a consulta o resultado não foi alterado.
A execução adiada é um comportamento padrão do LINQ em todas as suas implementações : LINQ to SQL, LINQ to Objects, LINQ to Entities, LINQ to Xml, etc.
Assim, se você não compreender corretamente esse comportamento do LINQ, o resultado das suas consultas pode ser afetado e você nem perceber o motivo pelo qual isso ocorreu.
Mas faço-vos saber, irmãos, que o
evangelho que por mim foi anunciado não é segundo os homens.
Porque não o recebi, nem aprendi de homem algum, mas pela revelação de Jesus
Cristo.
Gálatas 1:11-12
Referências: