C# - Apresentando Expression Trees - II


  No artigo de hoje vou apresentar o conceito de Expression Trees na linguagem C#.

Continuando o artigo anterior vamos analisar o comportamento das consultas LINQ.

As Expression Tree e os tipos IEnumerable<T> e IQueryable<T>

Na LINQ, uma expressão de consulta é compilada para árvores de expressão ou para delegates, dependendo do tipo aplicado ao resultado da consulta.

1- As consultas LINQ do tipo IEnumerable<T> são compiladas para delegates;

2- As consultas do tipo IQueryable ou IQueryable <T> são compiladas para árvores de expressão;

Em resumo, consultas LINQ executadas em processo são compiladas em delegates enquanto as consultas que são executadas fora do processo são compiladas em árvores de expressão.

Como na LINQ, consultas específicas de domínio (como LINQ to SQL, LINQ to Entity) resultam no tipo IQueryable<T>, o compilador C# compila essas consultas em código que cria uma árvore de expessão em tempo de execução. Em seguida, o provedor de consulta percorre a árvore de expressão e a traduz em uma linguagem de consulta, que é apropriada para essa fonte de dados.

Assim, as consultas LINQ-To-SQL ou Linq-To-Entities não são executadas no mesmo domínio da aplicação.

Vejamos um exemplo de consulta para Entity Framework:

 var query = from a in dbContext.Alunos
                   where a.Idade >= 18
                   select a;

Esta consulta na verdade não é executada no seu programa. Primeiro ela é traduzia para a seguinte instrução SQL e então executada no servidor SQL Server:

  1. SELECT [t0].[Nome], [t0].[Idade]
  2. FROM [dbo].[Alunos] AS [t0]
  3. WHERE [t0].[Idade] >= @p0

Dessa forma, primeiro o código é traduzido em uma instrução SQL e, em seguida, executado no servidor de banco de dados.

O código encontrado em uma expressão de consulta deve ser traduzido em uma consulta SQL que pode ser enviada para outro processo como uma string. Para o LINQ-to-SQL ou Entity Framework, esse processo é um banco de dados do SQL Server.

Naturalmente, será muito mais fácil traduzir uma estrutura de dados, como uma árvore de expressão, para SQL, do que traduzir o código bruto ou o código executável em SQL, pois, como você viu, é fácil recuperar informações de uma expressão.

Dessa forma, as Expression Trees foram criadas para a tarefa de converter código, como uma expressão de consulta, em uma string que pode ser passada para outro processo sendo ali executada.

A classe estática Queryable inclui métodos de extensão que aceitam um parâmetro Predicate do tipo Expression. Essa Expression de Predicate será convertida em uma Árvore de Expressão e, em seguida, será passada para o provedor LINQ remoto como uma estrutura de dados para que o provedor possa criar uma consulta apropriada a partir da árvore de expressão e executar a consulta.  

Veja abaixo a definição de IQueryable:

    public interface IQueryable : IEnumerable
    {
       Type ElementType { get; }
       Expression Expression { get; }
       IQueryProvider Provider { get; }
    }

Como você pode ver, o IQueryable contém uma propriedade do tipo Expression que contém a árvore de expressão e representa uma estrutura de dados equivalente ao código executável encontrado em uma expressão de consulta.

A propriedade de tipo IQueryProvider mantém o provedor LINQ (como LINQ to SQL, LINQ to entities etc.) e com base na consulta de provedor LINQ é convertida em uma consulta apropriada. A propriedade Type representa o tipo de elemento(s) que são retornados quando a árvore de expressão é executada.

Na figura abaixo vemos o fluxo desse processo:

 

Um mal-entendido comum é que as árvores de expressão são idênticas às expressões lambda. Mas isso não é verdade, pois você também pode criar e modificar árvores de expressão usando métodos de API, ou seja, sem usar a sintaxe de expressão lambda.

Além disso, toda expressão lambda não pode ser implicitamente convertida em uma árvore de expressão. Uma expressão lambda de múltiplas linhas não pode ser implicitamente convertida em árvore de expressão.

Na próxima parte do artigo veremos um exemplo prático usando Expression Trees.

"E disse Jesus: As raposas têm covis, e as aves do céu têm ninhos, mas o Filho do homem não tem onde reclinar a cabeça."
Mateus 8:20

Referências:


José Carlos Macoratti