LINQ com Lambda Expressions

LINQ é um conjunto de métodos e operadores da linguagem C# cujo objetivo é permitir a realização de consultas a coleções de objetos. Por meio desse recurso é possível efetuar tarefas comuns como localizar um objeto, somar e contar dados, ordenar listas, etc.

Um dos usos mais comuns do LINQ é na realização de consultas a bancos de dados usando o Entity Framework. Uma vez que esse framework ORM abstrai a estrutura do banco (tabelas, colunas e linhas) e a representa como objetos e listas em nossa aplicação, podemos efetuar diversas operações como filtro e ordenação usando apenas funções e expressões lambda, sem escrever código SQL. Veja no vídeo abaixo alguns exemplos de consultas comuns:

Neste vídeo usamos uma lista de objetos proveniente do Mapeamento Objeto-Relacional feito com o Entity Framework. Porém qualquer lista de objetos pode ser consultada via LINQ. A seguir você entenderá cada uma das consultas demonstradas. No entanto, antes é importante destacar que para que os métodos demonstrados aqui funcionem é necessário importar o namespace System.Linq.

Ordenação

O principal método para ordenação de listas é o OrderBy, que recebe como parâmetro a propriedade pela qual a coleção deve ser ordenada, por exemplo:

var cursos = db.Cursos.OrderBy(c => c.DataPublicacao);

Aqui a variável cursos está recebendo todos os registros da coleção db.Cursos, porém já ordenados pela data de publicação. É importante destacar que esse método não afeta a coleção original, ao invés disso ele retorna uma nova lista ordenada.

Para ordenar de forma decrescente podemos usar o método OrderByDescending, da seguinte forma:

var cursos = db.Cursos.OrderByDescending(c => c.DataPublicacao);

Também é possível ordenar por mais de uma propriedade. Para isso, para a primeira ordenação devemos usar o método OrderBy (ou OrderByDescending) e em seguida os métodos ThenBy ou ThenByDescending. Por exemplo, se desejarmos ordenar os registros pela data de publicação e em seguida pela carga horária, podemos proceder da seguinte forma:

var cursos = db.Cursos.OrderBy(c => c.DataPublicacao)
      .ThenBy(c => c.CargaHoraria);
       
    var cursos = db.Cursos.OrderBy(c => c.DataPublicacao)
      .ThenByDescending(c => c.CargaHoraria);

Caso a primeira ordenação deva ser decrescente, basta usar o OrderByDescending.

Filtros

O principal método para filtrar coleções é o Where, que recebe como parâmetro uma expressão representando o filtro a ser aplicado. Essa expressão representa um delegate, ou seja, uma referência para um método que espera como parâmetro um objeto do tipo armazenado pela lista (um curso, nesse caso) e retorna um booleano indicando se esse objeto atende a uma condição.

Filtros

Veja abaixo um exemplo de uso do Where no qual filtramos apenas os cursos do canal Python. Neste caso a propriedade Canal é um enum.

var cursos = db.Cursos.Where(c => c.Canal == Canal.Python);

Observe no parâmetro do método Where: usamos uma expressão lambda para atender ao delegate esperado. O argumento c representa cada curso na lista e à direita do operador lambda (=>) temos uma expressão booleana que deve ser atendida para que o item seja incluído na lista resultante.

A seguir temos mais alguns exemplos de filtro com esse método:


      //cursos com carga horária maior que 10h
      var cursos = db.Cursos.Where(c => c.CargaHoraria > 10);
       
      //cursos cujo título contém o termo "JSF"
      var cursos = db.Cursos.Where(c => c.Titulo.Contains("JSF"));

Também é possível usar os métodos do LINQ em conjunto. Por exemplo, após filtrar uma lista podemos ordená-la:

var cursos = db.Cursos.Where(c => c.CargaHoraria > 10)
        .OrderByDescending(c => c.DataPublicacao);

Funções de agregação

Funções de agregação como SUM, COUNT e AVG do SQL são muito utilizadas no contexto de bancos de dados. Com LINQ também é possível utilizá-las com coleções de objetos. Para isso temos vários métodos que podem ser vistos abaixo:

int soma = db.Cursos.Sum(c => c.CargaHoraria);

Aqui estamos somando a carga horária de todos os cursos.

int total = db.Cursos.Count(c => c.Canal == Canal.EngenhariaDeSoftware);

Neste exemplo estamos contando quantos cursos pertencem ao canal Engenharia de Software.

double media= db.Cursos.Average(c => c.CargaHoraria);

Com o método Average podemos calcular a média de valores. Neste caso estamos obtendo a média da carga horária dos cursos.

int max = db.Cursos.Max(c => c.CargaHoraria);
    int min = db.Cursos.Min(c => c.CargaHoraria);

Aqui estamos obtendo a maior e a menor carga horária dos cursos usando para isso as funções Min e Max.

Limitando a quantidade de registros

Muitas vezes desejamos obter apenas uma quantidade específica de registros de uma lista. Para isso podemos usar dois métodos Take ou FirstOrDefault. O primeiro retorna uma lista contendo um determinado número de itens passado por parâmetro, enquanto o segundo retorna o primeiro item da lista ou null caso a lista não possua itens.

var cursos = db.Cursos.OrderByDescending(c => c.DataPublicacao)
        .Take(5);

Nesse exemplo ordenamos a lista de cursos pela data de publicação de forma decrescente e pegamos apenas os 5 primeiros itens.

var curso = db.Cursos.OrderByDescending(c => c.DataPublicacao)
       .FirstOrDefault(c => c.Canal == Canal.PHP);

Aqui estamos pegando apenas o primeiro registro da lista ordenada, mas cujo canal é PHP. Ou seja, estamos pegando o curso mais recente de PHP.

Há ainda outros métodos e combinações possíveis entre eles que podem ser feitas a fim de atender cada necessidade. Para usá-los, é importante lembrar que é necessário importar o namespace System.Linq, onde estão declarados.