C# - Um pouco mais sobre LINQ Queries


Se você não conhece os conceitos básicos sobre LINQ leia meus artigos citados nas referências.

Neste artigo eu vou falar um pouco sobre LINQ Queries.

As unidades básicas de dados no LINQ são sequências e elementos.

Uma seqüência é qualquer objeto que implementa a interface IEnumerable<T> e um elemento é cada item na seqüência.

O exemplo a seguir temos uma sequência chamada nomes onde Macoratti, Jefferson e Miriam são os elementos.

Ex: string [] nomes = {"Macoratti", "Jefferson", "Miriam"};

Chamamos isso de uma seqüência local, porque representa uma coleção local de objetos em memória.

Um operador de consulta é um método que transforma uma seqüência. Um operador de consulta típica aceita uma seqüência de entrada e emite uma seqüência de saída transformada.

Na classe Enumerable do namespace System.Linq, existem cerca de 40 operadores, todos implementados como métodos de extensão estáticos. Estes são chamados de operadores de consulta padrão.

Uma consulta é uma expressão que transforma seqüências com operadores de consulta.

A consulta mais simples compreende uma seqüência de entrada e um operador. Por exemplo, nós podemos aplicar o operador Where em uma matriz simples para extrair aqueles elementos cuja tamanho seja de pelo menos 4 caracteres da seguinte forma:

Como os operadores de consulta padrão são implementados como métodos de extensão, podemos chamar o operador Wehre diretamente sobre nomes, como se fosse um método de instância:

IEnumerable<string> nomesFiltrados = nomes.Where(n => n.Length >= 4);

Podemos simplificar ainda mais o nosso código da seguinte forma:

var nomesFiltrados = nomes.Where(n => n.Length >= 4);

A maioria dos operadores de consulta aceita uma expressão lambda como um argumento. A expressão lambda ajuda a orientar e moldar a consulta. No nosso exemplo, a expressão lambda é a seguinte: n => n.Length >= 4

O argumento de entrada corresponde a um elemento de entrada. Neste caso, o argumento de entrada n representa cada nome na matriz e é do tipo string. O operador Where requer que a expressão lambda retorne um valor do tipo bool, o qual se for True, indica que o elemento deve ser incluído na sequência de saída. Aqui está a sua assinatura:

public static IEnumerable<TSource> Where<TSource> (this IEnumerable<TSource> source, Func<TSource,bool> predicate)

A consulta a seguir retorna todos os elementos que contém a letra 'a' :

var nomesFiltrados = nomes.Where(n => n.Contains("a"));

Até agora, criamos consultas usando métodos de extensão e expressões lambda.

Esta estratégia é altamente combinável na medida em que permite o encadeamento de operadores de consulta. Esta é conhecida como sintaxe fluente.

A linguagem C# também fornece outra sintaxe para escrever consultas, chamada de sintaxe de expressão de consulta. Para o exemplo acima se usarmos a sintaxe de expressão de consulta teríamos:

var nomesFiltrados = from n in nomes
                                    where n.Contains("a")
                                    select n;

A sintaxe fluente é mais flexível e básica. Vamos então abordar alguns exemplos usando essa sintaxe.

Encadeamenteo de operadores de consulta

Para criar consultas mais complexas, podemos acrescentar operadores de consulta adicionais na expressão de consulta criando uma corrente de operadores.

Para ilustrar vejamos um exemplo de consulta que extrai todas as strings contendo a letra 'a' , as ordena pelo tamanho e então converte o resultado para caixa alta (maiúsculas):

No nosso exemplo a variável n possui um escopo privado para cada uma das expressões lambdas.

Os operadores de consulta Where, OrderBy e Select são operadores de consulta padrão que resolvem para os métodos de extensão na classe classe Enumerable(namespace System.Linq).

  1. O operador Where emite uma versão filtrada da sequencia de entrada;
  2. O operador OrderBy emite uma versão ordenada de sua seqüência de entrada;
  3. O método Select emite uma seqüência onde cada elemento de entrada é transformado com uma dada expressão lambda (n.ToUpper(), neste caso).

Os dados fluem a partir de esquerda para a direita ao longo da cadeia de operadores, de forma que os dados são primeiro filtrados, em seguida, classificados e então transformado.

Quando operadores de consulta são encadeados, como neste exemplo, a sequência de saída de um operador é a sequência de entrada do próximo. O resultado final assemelha-se a produção linha de montagem como ilustrado na figura abaixo:

Podemos construir uma consulta semelhante de forma progressiva conforme o código abaixo:

IEnumerable<string> filtrados = nomes.Where(n => n.Contains("a"));
IEnumerable<string> ordenados = filtrados.OrderBy(n => n.Length);
IEnumerable<string> resultado = ordenados.Select(n => n.ToUpper());
     var filtrados = nomes.Where(n => n.Contains("a"));
      var ordenados = filtrados.OrderBy(n => n.Length);
      var resultado = ordenados.Select(n => n.ToUpper());
Sintaxe para consulta progressiva. As duas formas são equivalentes.

O resultado será o mesmo do obtido no exemplo anterior, onde vemos que cada consulta intermediária inclui uma consulta válida que podemos executar e exibir:

Ordenação Natural

A ordem original de elementos dentro de uma sequência de entrada é significativa em LINQ. Alguns operadores de consulta contam com esse comportamento, tais como Take, Skip e Reverse.

O operador Take(x) extrai os primeiros x elementos , descartando o resto:

O operador Skip(x) ignora os primeiros x elementos e extrai o resto:

O operador Reverse inverte a ordem dos elementos:

Operadores como Where e Select preservam a ordem original da seqüência de entrada. (A LINQ preserva a ordenação dos elementos na seqüência de entrada sempre que possível.)

Nem todos os operadores retornam uma sequência, os operadores de elementos extraem elementos a partir da sequência de entrada.

Exemplo de operadores de elementos: First, Last, ElementAt.

Os operadores de agregação retornam um valor escalar geralmente do tipo numérico:

Exemplo de operadores de agregação: Count(), Min(), Sum():

 

Pegue o projeto completo aqui: LinqQueries.zip

Joã 14:1 Não se turbe o vosso coração; credes em Deus, crede também em mim.

Joã 14:2 Na casa de meu Pai há muitas moradas; se não fosse assim, eu vo-lo teria dito; vou preparar-vos lugar.

Joã 14:3 E, se eu for e vos preparar lugar, virei outra vez, e vos tomarei para mim mesmo, para que onde eu estiver estejais vós também.

Joã 14:4 E para onde eu vou vós conheceis o caminho.

Joã 14:5 Disse-lhe Tomé: Senhor, não sabemos para onde vais; e como podemos saber o caminho?

Joã 14:6 Respondeu-lhe Jesus: Eu sou o caminho, e a verdade, e a vida; ninguém vem ao Pai, senão por mim.

Referências:


José Carlos Macoratti