Marco Baccaro

MCP • MCT • MCAD • MCSD • MCTS • MCPD Enterprise App Dev

Expression Linq


Já se faz bons anos desde a entrada da proposta de uma linguagem declarativa de consulta compilável, LINQ. As linguagens declarativas, o que são classificadas de linguagens de quarta geração, existe a bastante tempo. O SQL foi criado no início dos anos 70 pela IBM e padronizado como o padrão ANSI anos depois por um instituto americano. Nesse modelo declarativo, diferentemente do imperativo como C, C++, C#, nós não escrevemos como obter um objetivo, apenas declaramos nossas intenções. Por exemplo: “SELECT id, nome FROM TB_CLIENTE WHERE id > 10″. Nesse exemplo, não sabemos que tipo de algoritmo será executado, mas sabemos que atenderá nossa intenção, ou melhor, nossa declaração.Sou um grande admirador do LINQ, pois a idéia do Anders foi realmente boa, ter uma linguagem fortemente tipagem e com segurança contra erros banais em tempo de execução. Tendo essa linguagem, ficou até mais fácil e elegante acrescentar os provider para XML, Banco de Dados (ORM) entre outros, pois nada mais eram que uma espécie de repositório que uma linguagem de consulta declarativa estava pronta. Talvés para muitos, até aí nada de novo, apenas um pouco de cultura…rs. O que sempre fizemos foi implementar algoritmos para construir instruções declarativas em tempo de execução. Algumas de forma bem rudimentar permitindo (concatenação de string por exemplo) facilmente até ataque de SQL Injection. Nós temos essa construção em tempo de runtime porque muitas vezes dependemos das seleções ou entradas de dados do usuário final através de uma UI. Exemplo disso é qual o tipo de pesquisa para encontrar uma pessoa física, RG, CPF, parte do nome, sexo, idade, todos combinamos… e por aí vai.Para construir consultas em tempo de runtime com uma linguagem compilável como LINQ, precisamos utilizar as classes do namespace System.Linq.Expression. A Microsoft disponibilizou parte das classes que eles utilizam internamente para criar consultas e expressões lambda para nós. Com isso podemos montar filtros (condições where) entre outras em tempo de runtime e criar uma consulta LINQ dinamicamente.

Existe uma classe responsável para cada parte de uma expressão lógica. A tabela abaixo destaca as respectivas classes.

Expression Fornece um método estático para criar outros objetos de expressão.
BinaryExpression Representa uma expressão com uma operador binário. Ex: >, <, !=, == e etc.
ConstantExpression Representa uma referência constante em uma expressão. Ex: 50 de typeof(int), “Olá” de typeof(string) e etc.
MemberExpression Representa a referência de um membro (membro obtido por reflection). Ex: Propriedade qualquer de um objeto, x.Id > 10.
UnaryExpression Representa uma operação unária na expressão. Ex: Negativo de um valor.

A imagem abaixo ilustra a divisão lógica da expressão, no qual cada item é composto por uma classe.

Para ilustrar a utilização do consultas linq dinâmicas, criei um exemplo utilizando linq to object no qual a condição da filtragem (clausula where), será criado em tempo de execução. Para o exemplo criei uma entidade complex type chamado Student para o exemplo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ExpressionLinqSample {

   public class Student {

        public int Id { get; set; }
        public int Registry { get; set; }
        public string Name { get; set; }

    }

}

Nessa classe que denominei DynamicLinq, crio a expressão linq baseado nos parâmetros informados pelo usuário na UI:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;

namespace ExpressionLinqSample {

    public class DynamicLinq {

        static public Expression<Func> MakeExpression(int field, int paramFromUi) {

            Type type = typeof(Student);
            ParameterExpression paramExp = Expression.Parameter(type, "Student");
            MemberInfo studentyProperty = null;

            switch (field) {
                case 1:
                    studentyProperty = type.GetProperty("Id");
                    break;
                case 2:
                    studentyProperty = type.GetProperty("Registry");
                    break;
            }

            MemberExpression memberExp = Expression.MakeMemberAccess(paramExp, studentyProperty);
            ConstantExpression constExp = Expression.Constant(paramFromUi, typeof(int));
            BinaryExpression binExp = Expression.GreaterThanOrEqual(memberExp, constExp);
            return Expression<Func<Student, bool>>.Lambda<Func<Student, bool>>(binExp, paramExp);            

        }

    }

}

Esse é método Main de uma aplicação console para iniciar o programa e consumirmos o método de criação de consulta dinâmica. Veja que o expression delegate é criado a partir da consulta dinâmica criado. Como um delegate, ele executará o algoritmo confeccionado em runtime.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace ExpressionLinqSample {

    class Program {

        static void Main(string[] args) {

            List<Student> students = new List<Student>() {  
              new Student{Id = 10, Registry=100, Name="Paulo"},   
              new Student{Id = 20, Registry=200, Name="Pedro"},   
              new Student{Id = 50, Registry=300, Name="Felipe"},   
              new Student{Id = 60, Registry=400, Name="Lucas"} 
            };

            Expression<Func<Student, bool>> expression = DynamicLinq.MakeExpression(1, 50);      
            IEnumerable resultStudents = students.Where(expression.Compile());

            foreach (Student item in resultStudents) { 
               Console.WriteLine(item.Name);      
            }

        }

    }

}

Veja abaixo o conteúdo da instância do delegate chamado “expression” na janela Immediate do Visual Studio, nada mais que uma instrução lambda final:

De forma totalmente dinâmica criamos a condição linq “tal que Student.Id seja maior ou igual a 50″. E assim poderíamos ter criado inumeras outras condições que satisfaça uma regra de negócio, bem como formulário de pesquisa baseado nos filtros desejado pelo usuário. Até +.

6 Respostas para Expression Linq

  1. Dimara 02/01/2012 às 18:05

    Marco, parabéens pelo post como sempre enriquecendo o blog =) e também minha mente rs

  2. Guilherme Pimentel Pereira 09/01/2012 às 21:47

    Excelente Post Prof.!!Agradeço de antemão por ter aprendido bastante com vc no mês passado no módulo 10266 da Ka Solution nos módulos de sábado.Graças as suas aulas finais, finalmente aprendi Delegates,Lambdas,e outros assuntos do curso que eram lacunas grandes pra mim!!Gratidão eterna cara!!
    Gde Abraço!!

  3. David Coca 05/04/2012 às 13:27

    Tem que adicionar alguma referência ao projeto?
    Pois a declaração Expression esta retornando um erro.

    [Error 1 Using the generic type 'System.Func' requires 10 type arguments D:\ModuloDeRelatoriosLMS\ProjetosTestes\LinqExpression\LinqExpression\DinamicLinq.cs 14 34 LinqExpression]

    E quanto a declaração ?
    List students = new List()
    O Type List não é reconhecido!

  4. David Coca 05/04/2012 às 14:39

    Show de bola Marcos, e quanto ao erro na declaração
    Static public Expression MakeExpression(int field, int paramFromUi) {

    [Error 1 Using the generic type 'System.Func' requires 10 type arguments D:\ModuloDeRelatoriosLMS\ProjetosTestes\LinqExpression\LinqExpression\DinamicLinq.cs 14 34 LinqExpression]

  5. David Coca 09/04/2012 às 08:06

    Bom dia Marcos, não sei se entendeu a minha pergunta.
    Mas na declaração do método MakeExpression da classe DynamicLinq esta retornando um erro.

    static public Expression MakeExpression(int field, int paramFromUi) {

    ERROR:
    [Error 1 Using the generic type ‘System.Func’ requires 10 type arguments

    Abs

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Sair / Alterar )

Imagem do Twitter

You are commenting using your Twitter account. Sair / Alterar )

Foto do Facebook

You are commenting using your Facebook account. Sair / Alterar )

Connecting to %s

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.