O que é LINQ?

LINQ (Language-Integrated Query – Linguagem Integrada de Consulta) é, como define muito bem a documentação do MSDN, “um set de recursos introduzidos no Visual Studio 2008 que estende as poderosas capacidades de consulta SQL para as linguagens C# e Visual Basic. O LINQ introduz patterns facilmente aprendidos para consultar e atualizar dados, e uma tecnologia que pode ser estendida para suportar potencialmente qualquer tipo de armazenamento de dados.”. Acho que o próprio MSDN definiu muito bem o que é o LINQ não é?

O LINQ é considerado a “ponte” entre o “mundo dos objetos” e o “mundo dos dados”. A ideia com o LINQ foi tornar as consultas como um recurso de primeira classe nas linguagens de programação da plataforma .NET, sendo incorporado às linguagens C# e Visual Basic.

Como já escrito, o LINQ foi introduzido ao Visual Studio 2008, e com ele alguns recursos para suportar o LINQ nas linguagens. Entre os principais, temos a inclusão desses recursos:

  • Iniciadores de Objetos
  • Métodos de Extensão (Extensions Methods)
  • Expressões Lambda
  • Tipos Anônimos
  • Tipos Implícitos em Variáveis Locais (var)

Os LINQ Providers permitem as operações básicas do CRUD, de consulta, exclusão, atualização e inserção, além de permitir o mapeamento de tipos definidos pelo usuário. O .NET Framework suporta cinco providers para o LINQ, que são os seguintes:

Linq to SQL

O Linq to SQL fornece uma estrutura em runtime para gerenciar dados relacionais como objetos sem perder a habilidade de consulta. Assim, sua aplicação é livre para manipular objetos enquanto o Linq to SQL permanece em segundo plano, rastreando as alterações automaticamente. O Linq to SQL permite fazer o mapeamento objeto-relacional (ORM – Object-Relational Mapping), ou seja, permite mapear um modelo de dados de um banco de dados relacional para um modelo de objetos.

Linq to DataSet

O Linq to DataSet permite a consulta de objetos do tipo DataSet na memória. O diagrama da Figura 01 mostra a relação entre o Linq to DataSet e o ADO.NET 2.0:

Diagrama da relação do Linq To DataSet com o ADO.NET 2.0
Figura 01. Diagrama da relação do Linq To DataSet com o ADO.NET 2.0

Linq to Objects

Componente que permite consultar coleções de objetos do tipo IEnumerable diretamente. Resumindo, o Linq to Objects representa uma aproximação melhorada a coleções de objetos. Da maneira antiga, teríamos que fazer laços foreach complexos para especificar como recuperar dados de uma coleção. Com Linq to Objects, você escreve código declarativamente que descreverá o que você deseja recuperar.

Linq to XML

Componente que fornece uma interface de manipulação de XML em memória. O Linq to XML é parecido com o DOM (Document Object Model), você pode consultar e modificar o documento e, após isso, salvá-lo em um arquivo ou mesmo serializá-lo e enviar via email, por exemplo. Entretanto, o Linq To Xml difere do DOM, já que seu modelo de objeto é ligeiramente mais leve e rápido para trabalhar, pois tem como vantagem o uso das linguagens C# e Visual Basic.

Linq to Entities

O Linq to Entities, que é parte do ADO.NET Entity Framework, introduzido ao SP1 do .NET Framework 3.5, permite trabalhar com dados num alto nível de abstração do esquema relacional. O Entity Framework suporta o Entity Data Model (EDM) para definição dos dados num nível conceitual. O Linq to Entities é um recurso do Entity Framework, que permite escrever consultas ao modelo conceitual, usando as linguagens C# e Visual Basic.

PS: Os Linq Providers foram adicionados a partir da versão 3.5 do .NET Framework.


Expressões de Consulta LINQ

As expressões de consulta LINQ, introduzidas ao C# 3.0, são escritas com uma sintaxe declarativa que permite realizar com poucas linhas de código as operações de filtragem, ordenação, agrupamento, projeções, junção, entre outras.

Exemplo de expressões

As expressões devem ser iniciadas com from, depois podem ter um ou mais from, join, let, where ou até mesmo orderby e devem terminar com select ou group by.

Abaixo temos a sintaxe das expressões de consulta LINQ:

 from id in fonteDados

{ from id in fonteDados |

   join id in fonteDados on expressão equals

   expressão [into id] |

   let id = expressão |

   where condição |

  orderby id1, id2, ... [ascending|descending] }

  select expressão | group expressão by chave

[ into id ]

Na tabela abaixo vemos as palavras-chave do C# para as consultas LINQ, e sua descrição:

Cláusula

Descrição

from

Especifica a fonte de dados e uma variável de série (semelhante a uma variável de iteração em um laço foreach, por exemplo).

where

Filtra elementos da fonte de dados baseada em uma ou mais expressões booleanas.

select

Faz projeções, permitindo assim especificar o tipo e o formato dos elementos do resultado da consulta.

join

Junta duas fontes de dados baseado em comparações de igualdade entre dois elementos especificados.

in

Palavra-chave contextual, utilizada na cláusula join.

on

Palavra-chave contextual, utilizada na cláusula join.

equals

Palavra-chave contextual, utilizada na cláusula join. Ela substitui perfeitamente o operador == na comparação.

group

Agrupa os resultados de uma consulta de acordo com valores específicos de uma determinada chave.

by

Palavra-chave contextual, utilizada na cláusula group.

into

Fornece um identificador para servir de referência para os resultados de uma cláusula de junção (join), agrupamento (group) ou projeção (select).

orderby

Classifica os resultados em ordem ascendente ou descendente.

ascending

Palavra-chave contextual utilizado na cláusula orderby para determinar classificação em ordem ascendente, que é a padrão, caso a mesma seja omitida da sintaxe.

descending

Palavra-chave contextual utilizado na cláusula orderby para determinar classificação em ordem descendente.

let

Introduz uma variável para armazenar resultados de expressões intermediárias numa expressão de consulta. Assim, o resultado armazenado pode ser reutilizado em na consulta.

Mais informações sobre a sintaxe e a tabela podem ser encontradas neste artigo.

Para quem tem conhecimentos básicos de SQL não terá dificuldade ao usar o LINQ em seus projetos.

Exemplo Prático com LINQ

Para o projeto que será criado usaremos o banco de dados de exemplo Northwind, da Microsoft.

Antes de criar o projeto, devemos criar a conexão entre o Visual Studio e o Northwind. Para quem usa Windows 7 (como é o meu caso) o Visual Studio deve ser executado como administrador. Ainda sem projeto criado, abra a janela Server Explorer, clique com o botão direito em Data Connections e clique em Add Connection. Na nova tela, selecione seu servidor SQL, escolha o database Northwind e clique em OK.

Agora crie o projeto Windows Forms e adicione um ComboBox e um DataGridView. A ideia é popularmos nosso Grid com 20 produtos da tabela Products, do Northwind e utilizarmos o LINQ para filtrarmos os produtos de acordo com o valor selecionado no ComboBox.

Inicialmente vá à página de códigos do formulário e crie uma classe chamada Products, que terá algumas propriedades, referentes a colunas da tabela Products, do Northwind, como é ilustrado na Listagem 01:

Listagem 01. Classe Products com propriedades.

public class Products {
  public int ProductID {
    get;
    set;
  }
  public string ProductName {
    get;
    set;
  }
  public decimal UnitPrice {
    get;
    set;
  }
  public decimal UnitsInStock {
    get;
    set;
  }
}
        

Agora crie o método CarregarGridView, que será responsável por fazer a lógica de acesso a dados na tabela Products. Veja o código abaixo, na Listagem 02.

Listagem 02. Método para retornar os dados da tabela Products.

private List CarregarGridView() {
  List lstProducts = new List();
  string strConnectionString = @"Data Source=.\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True";
  string strInstrucaoSelect = "SELECT TOP 20 ProductID, ProductName, UnitPrice, UnitsInStock FROM Products";
  using(SqlConnection objConexao = new SqlConnection(strConnectionString)) {
    using(SqlCommand objCommand = new SqlCommand(strInstrucaoSelect, objConexao)) {
      objConexao.Open();
      SqlDataReader objDataReader = objCommand.ExecuteReader();
      if (objDataReader.HasRows) {
        while (objDataReader.Read()) {
          Products objProducts = new Products();
          objProducts.ProductID = Convert.ToInt32(objDataReader["ProductID"].ToString());
          objProducts.ProductName = objDataReader["ProductName"].ToString();
          objProducts.UnitPrice = Convert.ToDecimal(objDataReader["UnitPrice"].ToString());
          objProducts.UnitsInStock = Convert.ToDecimal(objDataReader["UnitsInStock"].ToString());
          lstProducts.Add(objProducts);
        }
        objDataReader.Close();
      }
    }
    objConexao.Close();
  }
  return lstProducts;
}

O que fiz acima basicamente foi criar um método do tipo List (lista genérica “tipada”), usando como tipo pra lista a classe Products, instanciar um objeto dessa classe (que será o retorno do método), atribuir o objeto de conexão e objeto de comando, passar a instrução SQL de SELECT, abrir a conexão, atribuir o retorno dos dados a um objeto do tipo SqlDataReader e, após isso, usar o laço while para iterar seus valores.

Dentro do while é instanciado um objeto do tipo da classe Products, atribuído os valores do objDataReader (vindos do banco) para as propriedades da classe e, finalmente, adicionado os valores armazenados no Products em minha lista, instanciada no começo do método. Finalizando é fechado o objDataReader e o objConexao e retornado a lista, com os dados carregados.

Volte ao formulário e dê dois cliques na parte cinza do mesmo para chamar seu evento Load. Dentro dele insira o código da Listagem 03.

Listagem 03. Evento Load do Formulário

private void Form1_Load(object sender, EventArgs e) {
  List lstProducts = new List();
  lstProducts = CarregarGridView();
  dgvProdutos.DataSource = lstProducts;
}

Dessa forma, ao rodar a aplicação são carregados os dados no Grid, como ilustra a Figura 02.

Dados da tabela Products
Figura 02. Dados da tabela Products

Continuando nosso exemplo, vá no modo Design do formulário, clique com o botão direito no ComboBox e clique em Edit Items. Na caixa de texto que surge adicione os seguintes itens, como é ilustrado na Figura 03:

Itens do ComboBox, que serão os filtros do GridView
Figura 03 Itens do ComboBox, que serão os filtros do GridView

Agora dê dois cliques em cima do ComboBox para que o evento SelectedIndexChanged seja acionado. Nele iremos montar toda a lógica para que, quando o usuário selecionar determinado item, seja passado a instrução LINQ ao DataSource do GridView.

Vemos abaixo, na Listagem 04, toda a codificação do evento.

Listagem 04. Evento SelectedIndexChanged do ComboBox

private void cboValores_SelectedIndexChanged(object sender, EventArgs e) {
  List lstProducts = new List();
  lstProducts = CarregarGridView();
  BindingSource objBindingSource = new BindingSource();
  switch (cboValores.SelectedIndex.ToString()) {
  case "0":
    objBindingSource.DataSource = from instrucao in lstProducts
    where instrucao.ProductName.StartsWith("Ch")
    select instrucao;
    dgvProdutos.DataSource = objBindingSource;
    break;
  case "1":
    objBindingSource.DataSource = from instrucao in lstProducts
    where instrucao.UnitsInStock > 15
    orderby instrucao.UnitsInStock
    select instrucao;
    dgvProdutos.DataSource = objBindingSource;
    break;
  case "2":
    objBindingSource.DataSource = from instrucao in lstProducts
    where instrucao.UnitPrice > 25
    orderby instrucao.UnitPrice
    select instrucao;
    dgvProdutos.DataSource = objBindingSource;
    break;
  case "3":
    objBindingSource.DataSource = from instrucao in lstProducts
    where instrucao.UnitsInStock < 50
    orderby instrucao.UnitsInStock descending
    select instrucao;
    dgvProdutos.DataSource = objBindingSource;
    break;
  case "4":
    objBindingSource.DataSource = from instrucao in lstProducts
    where instrucao.ProductName.EndsWith("s")
    select instrucao;
    dgvProdutos.DataSource = objBindingSource;
    break;
  default:
    dgvProdutos.DataSource = null;
    break;
  }
}

Note que começo instanciando a lista genérica do tipo da classe Products, vista anteriormente, e atribuo a ela o método CarregarGridView, assim tenho a lista carregada com os dados vindos do banco. Depois instancio um objeto da classe BindingSource, necessária para ser a fonte de dados de nosso GridView.

Em meu switch, uso como condição a propriedade SelectedIndex do ComboBox. Assim, dependendo do índice escolhido pelo usuário (lembrando que sempre começa em 0) é realizada a codificação do respectivo operador case.

Note que, dentro de todos os “cases”, a codificação é bem parecida: é atribuído a instrução LINQ a propriedade DataSource do objeto instanciado e o mesmo é passado como DataSource para o GridView.

Perceba também que a cláusula where que é a responsável por definir o filtro de minha busca, e em algumas instruções é usado também o operador orderby, usado para ordenar os registros, em ordem ascendente (padrão) ou descendente.

Finalizando, acrescente ao formulário um botão com o texto Limpar Filtros, com o código da Listagem 05.

Listagem 05. Botão Limpar Filtros

private void btnLimparFiltros_Click(object sender, EventArgs e) {
  List lstProducts = new List();
  lstProducts = CarregarGridView();
  dgvProdutos.DataSource = lstProducts;
}

Assim caso quisermos ver todos os registros após começarmos as filtragens é só clicarmos no botão.

Rode a aplicação e altere os valores do ComboBox. A Figura 04 ilustra o resultado para o filtro de produtos terminados com a letra S.

Filtro aplicado ao GridView
Figura 04. Filtro aplicado ao GridView
Aproveite e assine nossa Newsletter

Para saber mais sobre LINQ, visite este link e os conteúdos adjacentes.

Links Úteis

Saiba mais sobre LINQ ;)

  • Simplificando consultas com LINQ:
    Neste vídeo será feito uma abordagem de como montar consultas simplificadas de acesso a dados com a utilização do LINQ (Language Integrated Query).
  • C# LINQ: Criando um Sistema de Vestibular com POO:
    Nesse video veremos como programar de forma correta um sistema do mundo real, usando as melhores práticas de desenvolvimento OO e C#.
  • LINQ to SQL: Persistindo dados:
    Neste artigo veremos a simplicidade com que o LINQ to SQL efetua as operações de persistência e o controle das entidades dentro de um Modelo de Dados.