.NET

20 out, 2010

DYNAMIC LINQ – Consultas dinâmicas utilizando LINQ

Publicidade

O LINQ já é um recurso bem conhecido pelos adeptos da plataforma .NET, e está disponível desde o framework 3.5, por isso dispensa maiores comentários.

Apesar da intenção de fazer com que ele se assemelhasse à sintaxe SQL, o seu funcionamento é bem diferente. O LINQ escreve comandos a partir de tipos seguros em VB ou C#, ou seja, você obtém em tempo de compilação às suas consultas LINQ. Existem outras vantagens como o refactoring (processo de modificar um sistema de software para melhorar a estrutura interna do código sem alterar seu comportamento externo) e intellisense (muito útil quando precisamos navegar entre entidades).

Essas “vantagens” do LINQ podem lhe trazer uma dor de cabeça quando for necessário realizar customizações em seu projeto. Como por exemplo, um projeto de BI onde o seu cliente precisa de uma interface sendo que o próprio possa criar filtros customizados. Nos tempos em que gerávamos comandos texto para as consultas SQL, esse processo era comumente utilizado, agora eu lhe pergunto: você já imaginou como fazer isso com LINQ com a mesma simplicidade que se gerava comandos SQL? É exatamente isso que iremos aprender daqui por diante.

Implementação

Neste artigo, vou lhes apresentar uma forma muito simples de incrementar uma consulta dinâmica com LINQ. Trata-se de um conceito muito conhecido por aqueles que trabalharam com consultas SQL em projetos envolvendo Business Intelligence (BI), ou seja, através de manipulação e de concatenação de strings.

Veja abaixo um exemplo de consulta utilizando o LINQ convencional e em seguida outra consulta utilizando o Dynamic LINQ. O código de ambas as práticas está separado por comentários e pode ser facilmente compreendido.

Para criar consultas dinâmicas é necessário que você faça o download da biblioteca LINQ Dynamic Query, neste caso utilizaremos a versão C#, mas a library também está disponível em VB.

Esse recurso é disponibilizado pela própria Microsoft e pode ser utilizado em qualquer provedor de dados, ou seja, LINQ to SQL, LINQ to Objects, LINQ to XML, LINQ to Entities entre outros.

Para que tenhamos mais clareza sobre o funcionamento do Dynamic LINQ, vamos vislumbrar o seguinte cenário: você recebeu a tarefa de adicionar uma nova funcionalidade a um projeto de BI, esse requisito precisa de uma tela que exiba aos usuários uma lista de clientes e permita que o mesmo construa filtros conforme a sua necessidade; essa listagem deve apresentar código do cliente, nome, data de nascimento e sua renda mensal.

Começaremos pelo básico: primeiramente crie uma aplicação web. Depois, adicione a essa aplicação uma classe chamada Cliente, a classe deve estar estruturada da seguinte maneira:

Vamos criar um cenário que atenda ao requisito solicitado: adicione no WebForm um GridView responsável pela listagem dos clientes. Logo após isso, vá até o code-behind do WebForm onde você adicionou o GridView e implemente uma lista de clientes fictícia.

Agora basta atribuir à coleção LstCliente ao GridView e executar o projeto. A imagem a seguir está esboçando o resultado esperado. Você poderá acompanhar melhor o comportamento da aplicação através do arquivo disponibilizado para download.

Para controlar os filtros adicionados pelo usuário, vamos implementar uma classe que se chamará Filtro, ela será a responsável pelo comportamento dos filtros dinâmicos.

A tela em que serão listados os clientes receberá quatro novos controles, eles tem como objetivo trazer uma melhor interatividade entre o recurso de filtragem e o usuário. A seguir um detalhamento desses itens:

  • DropDownList permite que o usuário selecione em qual propriedade deseja executar o filtro.
  • TextBox onde o usuário deverá informar o valor que determinará o filtro.
  • Button para adicionar o filtro criado pelo usuário.
  • GridView que será responsável pela remoção e pela exibição dos filtros criados pelo usuário.

O resultado da tela está esboçado na imagem abaixo:

Agora voltamos aos eixos e focamos na principal proposta do artigo, ou seja, a elaboração de consultas dinâmicas através do LINQ (language integrated query).

O recurso que irá nos ajudar a obter o resultado esperado é a DynamicQueryable. Você pode encontrá-la dentro da biblioteca LINQ Dynamic Query, lembre-se de fazer o download dos itens informados anteriormente, descompacte o arquivo e vá até a pasta CSharpSamples e logo após adicione a classe ao projeto. Ela estará armazenada em CSharpSamples > LinqSamples > DynamicQuery > DynamicQuery > Dynamic.cs

No próprio code-behind da tela iremos adicionar dois métodos com responsabilidade direta pela composição dos filtros, o primeiro método chama-se GetQuery e tem como finalidade compor a Query através da concatenação de string. Veja abaixo a estrutura do método:

Detalhamento dos parâmetros consumidos pelo método esboçado na imagem acima:

  • Parâmetro tipo – é responsável por receber o tipo da propriedade que será filtrada. 
  • Coluna – deve armazenar o nome da coluna. 
  • Posicao – tem como objetivo armazenar a posição exata do parâmetro passado para a cláusula where atribuída ao DynamicQuery

O motivo da existência dos três parâmetros consumidos pelo método acima é a exigência do LINQ pela utilização de tipos seguros na construção de suas expressões. Isso ocorre porque a construção de consultas LINQ é baseada em tipos seguros escritos em vb ou C#, sendo assim a tipagem dos objetos se torna obrigatória mesmo quando geramos uma consulta através de comandos textos baseada na manipulação strings, a DynamicQueryable apenas traduz esses comandos texto em expressões lambda.

Como explicado no parágrafo anterior, as consultas LINQ são baseadas em tipos seguros, então se você tentar efetuar uma pesquisa de um tipo string em uma propriedade (coluna) do tipo DateTime acabará não obtendo êxito, mesmo formatando essa data corretamente a Library não fará a conversão automaticamente. Por isso, a propriedade Valor da classe Filtro é um object, ela receberá um valor e terá o seu tipo atribuído através do método ParseObject.

Agora que já tivemos uma compreensão parcial do funcionamento de consultas dinâmicas através do LINQ, podemos ter uma prévia do funcionamento da tela proposta anteriormente. Acompanhe abaixo um print da tela.

Destacado na imagem acima está a cláusula where gerada dinamicamente pelo usuário, e, conforme explicado anteriormente, você pode perceber que o comando texto está dividido pelas propriedades (colunas), os seus respectivos tipos e a posição ocupada por cada uma no array de valores submetidos à pesquisa.

Encerramento

A DynamicQueryable não lhe permite apenas a elaboração de consultas, uma gama de outros recursos pode ser explorada, você pode executar outras operações como Order By, Skip, Take, etc.

Infelizmente não há suporte direto a associações, como por exemplo, quando for necessária a utilização de JOIN.

Ao construir queries dinâmicas através da concatenação de strings para formar uma cadeia de expressão, temos grandes chances de gerar erros de análise.

A Dynamic Query oferece classes ParseException, que irão lhe trazer maior comodidade ao  capturar os erros gerados por suas expressões. 

Creio que o artigo não tenha sintetizado com toda clareza necessária todos os benefícios da DynamicQueryable. Você pode baixar o código-fonte do artigo e obter mais informações sobre os dois assuntos.

Obrigado a todos!

Referências

Requisitos:

  • Visual Studio 2008 SP1
  • Framework 3.5 SP1

Código-fonte para download.