Desenvolvimento - C#
Lambda Expressions x SQL: Comparando a sintaxe de consultas comuns
Veja neste artigo como realizar algumas das consultas mais comuns com Lambda Expressions. Veremos uma comparação da sintaxe em C# com a equivalente em SQL, utilizando os principais operadores de comparação.
por Joel RodriguesAs chamadas Lambda Expressions, ou Expressões Lambda, são parte integrante da LINQ (Language Integrated Query), um recurso do .NET Framework cuja função é realizar consultas a listas de objetos, independentemente de seu tipo. Utilizando esta sintaxe, é possível filtrar conjuntos de dados realizando operações complexas de avaliação, tais como as que são realizadas no banco de dados utilizando SQL.
Neste artigo veremos como realizar os principais tipos de consultas utilizando Lambda Expressions, sempre comparando com a consulta equivalente que poderia ser feita com SQL diretamente no banco de dados. O objetivo aqui não é explicar a teoria relacionada a LINQ, mas sim apresentar na prática algumas consultas muito utilizadas.
Para os exemplos que aqui serão desenvolvidos, utilizaremos uma classe bastante simples, que facilmente poderia ser transformada em uma tabela de banco de dados com estrutura equivalente. O código que será apresentado aqui independe de plataforma, portanto o exemplo pode ser desenvolvido em qualquer tipo de aplicação (Windows Forms, WPF, ASP.NET, etc). Aqui, porém, utilizaremos uma aplicação Windows Forms, na qual adicionaremos uma classe cujo código pode ser visto na Listagem 1.
Listagem 1: Classe Produto
public class Produto { public int Codigo { get; set; } public string Descricao { get; set; } public decimal Preco { get; set; } public DateTime UltimaEntrada { get; set; } }
No formulário principal, declararemos uma variável do tipo Listlt;Produto>, que chamaremos de produtos. Esta lista servirá para armazenar os dados que serão filtrados posteriormente. Em uma aplicação real, esta lista poderia ser uma entidade mapeada pelo Entity Framework, por exemplo, contendo dados reais do banco de dados.
No construtor do formulário, vamos gerar alguns dados para teste, conforme mostra a Listagem 2.
Listagem 2: Gerando dados para teste
produtos.Add(new Produto() { Codigo = 1234, Descricao = "Nokia Lumia 1020", Preco = 1000, UltimaEntrada = DateTime.Today.AddDays(-30)}); produtos.Add(new Produto() { Codigo = 4321, Descricao = "Microsoft Surface 2", Preco = 2000, UltimaEntrada = DateTime.Today.AddDays(-20) }); produtos.Add(new Produto() { Codigo = 4567, Descricao = "Mouse Optico Sem Fio", Preco = 50, UltimaEntrada = DateTime.Today.AddDays(-60) }); produtos.Add(new Produto() { Codigo = 7564, Descricao = "iPhone 5S", Preco = 1999, UltimaEntrada = DateTime.Today.AddDays(-15) }); produtos.Add(new Produto() { Codigo = 8897, Descricao = "Teclado QWERTY Wireless", Preco = 80, UltimaEntrada = DateTime.Today.AddDays(-80) }); produtos.Add(new Produto() { Codigo = 9876, Descricao = "Case para notebook", Preco = 43, UltimaEntrada = DateTime.Today.AddDays(-50) }); produtos.Add(new Produto() { Codigo = 5432, Descricao = "Samsumg Galaxy Note", Preco = 1799, UltimaEntrada = DateTime.Today.AddDays(-5) }); produtos.Add(new Produto() { Codigo = 1020, Descricao = "Multifuncional HP", Preco = 589, UltimaEntrada = DateTime.Today}) ;
Na interface, teremos apenas um Button e um DataGridView. Atribuindo a lista produtos à propriedade DataSource do DataGridView, teremos um resultado semelhante ao que mostra a Figura 1.
Figura 1: Interface do exemplo em funcionamento
Como seria sem LINQ
A melhor forma para entender a eficiência de uma ferramenta, é saber como seria o trabalho caso ela não existisse. Por isso, consideremos o seguinte cenário: em uma tela de consulta, precisamos filtrar todos os produtos cujo preço esteja dentro de uma faixa.
Inicialmente, para localizar esses produtos precisaríamos realizar um loop, iterando sobre a lista original e verificando quais itens atendem à condição estabelecida. O código seria algo como o que vemos na Listagem 3.
Listagem 3: Filtrando a lista através de um loop
// Equivalente a SELECT * FROM Produtos WHERE PRECO > 500 AND PRECO < 1900 List<Produto> resultado = new List<Produto>(); foreach (Produto p in produtos) { if (p.Preco > 500 && p.Preco < 1900) resultado.Add(p); } dataGridView1.DataSource = resultado;
De fato esse código não é complexo, mas à medida que o projeto cresce e precisamos adicionar novos filtros, por exemplo, fica claro que o código se tornará bem mais extenso e repetitivo. Com Lambda, esse tipo de procedimento pode ser realizado com uma sintaxe bem mais resumida, utilizando métodos diretamente sobre a lista de origem, e já retornando uma coleção com o resultado.
Filtrando coleções com Lambda Expressions
Para utilizar LINQ, primeiramente é necessário referenciar o namespace System.Linq na seção de usings. A partir daí, já poderemos acessar os métodos de extensão da LINQ existentes para listas. Podemos então partir para a parte prática.
As consultas serão realizadas utilizando o método Where, que recebe como parâmetro uma expressão indicando as condições de retorno e retorna um IEnumerable com o mesmo tipo de item contido na lista original. Ou seja, nos nossos exemplos, o método Where retornará IEnumerable<Produto>.
O primeiro filtro que realizaremos utilizará a comparação de igualdade, ou seja, equivalente ao operador = em consultas SQL. Aqui desejamos obter apenas o produto cujo código é igual a 1234. Na Listagem 4 observamos como é simples realizar essa consulta. Para ver o resultado, basta atribuir o resultado à propriedade DataSource do DataGridView.
Listagem 4: Comparação de igualdade
// Equivalente a SELECT * FROM Produtos WHERE Codigo = 1234 var resultado = produtos.Where(p => p.Codigo == 1234);
No método Where, o identificador p funciona como um alias (apelido) e representa cada item da lista, ou seja, um objeto do tipo Produto.
As próximas consultas equivalem ao operador LIKE da SQL. Na Listagem 5 utilizamos o método Contains da classe String, que retorna true quando uma variável contém o texto passado como parâmetro. Porém, este método é CASE SENSITIVE, ou seja, diferencia letras maiúsculas de minúsculas. Para resolver isso, podemos converter os dois dados da comparação para maiúsculo, utilizando o método ToUpper, ou para minúsculo, com o ToLower (Listagem 6).
Listagem 5: Operação do tipo LIKE (contendo o texto)
// Equivalente a SELECT * FROM Produtos WHERE Descricao LIKE ‘%note%’ var resultado = produtos.Where(p => p.Descricao.Contains("note"));
Listagem 6: Operação do tipo LIKE case insensitive
// Equivalente a SELECT * FROM Produtos WHERE Descricao LIKE ‘%NOTE%’ var resultado = produtos.Where(p => p.Descricao.ToUpper().Contains("NOTE"));
O LIKE também pode ser usado para identificar as colunas que COMEÇAM COM um determinado texto, ou que TERMINAM COM um certo texto. Em C#, a classe String também possui os métodos StartsWith (começa com, na Listagem 7) e EndsWith (termina com, na Listagem 8).
Listagem 7: Operação do tipo LIKE (começando com)
// Equivalente a SELECT * FROM Produtos WHERE Descricao LIKE ‘Multi%’ var resultado = produtos.Where(p => p.Descricao.StartsWith("Multi"));
Listagem 8: Operação do tipo LIKE (terminando com)
// Equivalente a SELECT * FROM Produtos WHERE Descricao LIKE ‘%Fio’ var resultado = produtos.Where(p => p.Descricao.EndsWith("Fio"));
Por fim, outras comparações também muito utilizadas são as de desigualdade, ou seja, aquelas em que aplicamos os operadores MAIOR QUE, MENOR QUE e BETWEEN (que equivale a MAIOR OU IGUAL e MENOR OU IGUAL em conjunto). Na Listagem 9 fazemos um filtro semelhante ao que vimos na Listagem 3 . Aqui desejamos obter os produtos cujo preço estejam na faixa de 500,00 a 1900,00 (incluindo esses limites), operação que em SQL é feita com o operador BETWEEN.
Listagem 9: Operação do tipo BETWEEN
// Equivalente a SELECT * FROM Produtos WHERE Preco BETWEEN 500 AND 1900 var resultado = produtos.Where(p => p.Preco >= 500 && p.Preco =< 1900);
Nota: Para testar os códigos, o leitor pode inserir a linha dataGridView1.DataSource = resultado.ToList() após cada consulta.
Observe que a utilização de LINQ, através de expressões Lambda, garantem agilidade no desenvolvimento do código, uma vez que não é necessário realizar loops explicitamente, pois todas as operações são feitas a partir de métodos, cujo retorno já tem o tipo adequado ao da coleção original.
O leitor pode explorar outras opções, testando os demais operadores de comparação e aumentando a complexidade das consultas.
Aqui chegamos ao fim deste artigo. Até a próxima.