.NET - Apresetando LINQ To SQL - III


"It's about turning query set operations and transforms into first-class concepts of the language"

Foi com esta frase que Anders Hejlsberg , o arquiteto chefe da linguagem C#, definiu o LINQ - Language Integrated Query.

A LINQ apresenta uma sintaxe usada nas linguagens funcionais como a linguagem SQL de forma a tornar mais intuitiva e simples o acesso aos dados.

O propósito deste artigo é expor uma visão geral do papel do LINQ To SQL e sua integração com banco de dados em aplicações Visual Basic. Vou mostrar como as classes são mapeadas para os banco de dados e como refinar o processo de mapeamento. Como a jornada é longa vamos por partes.

Material necessário:

1- Visual Basic 2008 Express Editon ou Visual Studio 2008
2- Banco de dados Northwind.mdf

Criando um modelo de Objetos usando o descritor Visual LINQ

Podemos criar um modelo de objetos mapeados para o banco de dados via código mas usando o descritor Visual LINQ é tarefa é mais rápida e fácil. Então vamos a ela:

Criando um projeto Linq

- Abra o VB 2008 Express e clique no menu File|New Project;

A seguir selecione o template Windows Forms Application e informe o nome LINQ_SQL_VB1 e clique em OK;

Agora clique no menu Project|Add New Item (CTRL+Shift+A) e a seguir selecione LINQ to SQL Classes e informe o nome Northwind.dbml;

Agora dê uma espiada na janela Solution Explorer; clique no ícone Show all Files e você verá as referências para o assembly System.Data.Linq e o arquivo Northwind.dbml contendo o descritor LINQ e o código gerado no processo.

Abra o DataBase Explorer no menu View | DataBase Explorer e verifique se já existe uma conexão com o banco de dados Northwind. Se ela não existir clique com o botão direito do mouse sobre a o ícone Data Connections e selecione Add Connection. Selecione o Data Source - Microsoft SQL Server DataBase File e no botão Browse localize o banco de dados Northwind.mdf na sua máquina local.

Ao final do processo a sua janela DataBase Explorer deverá exibir a conexão com o Northwind.mdf exibindo os objetos do banco de dados conforme abaixo:

Expanda os objetos Tables, selecione e arraste para o descritor LINQ as seguintes tabelas: Customers, Orders e Employees.

Você verá no descritor LINQ as classes das entidades: Customer, Order e Employee geradas e se clicar no arquivo Northwind.designer.vb verá o código gerado pelo mesmo.

Agora expanda os objetos Stored Procedures na janela DataBase Explorer e selecione a stored procedure Ten Most Expensive Products e arraste para a janela Methods do Descritor LINQ (Se não estiver visualizando a janela, clique com o botão direito sobre o descritor e selecione Show Methods Pane)

A Stored Procedure Ten Most Expensive Products possui o seguinte código:


SELECT Products.ProductName AS TenMostExpensiveProducts, Products.UnitPrice
FROM Products
ORDER BY Products.UnitPrice DESC

Ela selecione o nome do produto e o preço unitário da tabela Products e ordena por preço unitário em ordem decrescente.

Vamos agora criar a interface no formulário form1.vb com dois ListBox e um botão de comando conforme leiaute abaixo:

No evento Click do botão de comando Executar vamos inserir o código abaixo:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'instanciamos a classe DataContext que já tem mapeado os objetos e a conexão
        Dim db As New NorthwindDataContext

        'selecione os clientes de Londres
        Dim clientes = From customer In db.Customers _
                       Where customer.City = "London" _
                       Select customer

        'cabeçalho do ListBox1
        ListBox1.Items.Add("Clientes de Londres")
        ListBox1.Items.Add("-------------------------")
        ListBox1.Items.Add("Cliente - Total")
        ListBox1.Items.Add("-------------------------")

        'exibe os clientes de Londres e o total de clientes
        For Each cliente In clientes
            ListBox1.Items.Add(cliente.CustomerID & " - " & cliente.Orders.Count)
        Next

        'cabeçalho do ListBox2
        ListBox2.Items.Add("Os dez produtos mais vendidos")
        ListBox2.Items.Add("----------------------------------")
        ListBox2.Items.Add("Nome do Produto - Preço Unitário")
        ListBox2.Items.Add("----------------------------------")

        'exibe o nome do produto e o preço unitário
        For Each produto In db.Ten_Most_Expensive_Products
            ListBox2.Items.Add(produto.TenMostExpensiveProducts & " / " & produto.UnitPrice)
        Next

    End Sub

Ao digitar o código você vai perceber que o recurso IntelliSense exibirá os mapeamentos para os objetos. Você verá o mapeamento para a stored procedure Ten_Most_Expensive_Products como um método DataContext. Verá também que o descritor criou o tipo Ten_Most_Expensive_Products contendo duas propriedades mapeadas para os campos retornadas pela stored procedure.

Executando o projeto iremos obter:

Vamos examinar o código:

Comecemos com a declaração:

'instanciamos a classe DataContext que já tem mapeado os objetos e a conexão
Dim db As New NorthwindDataContext

Este código cria um objeto NorthwindDataContext que representa uma conexão fortemente tipada com o banco de dados. O nome NorthwindDataContext é baseado no nome informado para o arquivo .dbml acrescentando a palavra DataContext ao final do mesmo.

Examinando a instrução LINQ:

'selecione os clientes de Londres
Dim
clientes = From customer In db.Customers _
                     Where customer.City = "London" _
                     Select customer

iremos perceber que do lado esquerdo da atribuição estamos declarando uma variável chamada clientes sem tipo definido.

Isto é possível devido a uma das novas características da plataforma .NET 3.5 : Inferência de tipos, onde a informação de tipo será , quando possível, inferida diretamente pelo compilador.

O lado direito da atribuição temos uma expressão de consulta que é uma extensão da linguagem introduzida pelo projeto LINQ. A inferência de tipos esta sendo usada aqui para simplificar o código.

A inferência de tipos também é usada no comando For/Each pois não estamos especificando um tipo para a variável cliente.

'exibe os clientes de Londres e o total de clientes
For Each
cliente In clientes
      ListBox1.Items.Add(
cliente.CustomerID & " - " & cliente.Orders.Count)
Next

Observe que o total de clientes é obtido pelo LINQ atráves do código: cliente.Orders.Count

No código abaixo:

'exibe o nome do produto e o preço unitário
For Each produto In db.Ten_Most_Expensive_Products
    ListBox2.Items.Add(
produto.TenMostExpensiveProducts & " / " & produto.UnitPrice)
Next

Temos o método Ten_Most_Expensive_Products mapeado da stored procedure Ten Most Expensive Products.

Agregando Dados usando os operadores do LINQ

Podemos facilmente usar os operadores de consulta LINQ para realizar consultas de dados agregados. Para poder realizar as consultas a seguir volte a janela DataBase Explorer e arraste a tabela Products para o descrito LINQ para gerar o mapeamento para a classe da entidade Product.

A seguir vamos incluir um novo ListBox no formulário e um botão de comando. A seguir inclua o código abaixo no evento Click do botão de comando:

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        'instanciamos a classe DataContext que já tem mapeado os objetos e a conexão
        Dim db As New NorthwindDataContext

        'calcula o custo médio dos produtos com letra inicial B
        Dim custoMedio = Aggregate produto In db.Products _
                                  Where produto.ProductName.StartsWith("B") _
                                  Select produto.UnitPrice _
                                  Into Average()

        'cabeçalho do ListBox3
        ListBox3.Items.Add("Produtos que iniciam com a letra B")
        ListBox3.Items.Add("------------------------------------")

        ListBox3.Items.Add(" Custo médio = " & custoMedio)

    End Sub

Pressionando F5 para executar iremos obter:

Vamos examinar a instrução LINQ:

Dim custoMedio = Aggregate produto In db.Products _
                          Where produto.ProductName.StartsWith("B") _
                          Select produto.UnitPrice _
                          Into Average()

O resultado obtido será o custo médio dos produtos iniciados com a letra "B".

Primeiro especificamos a tabela db.Products e restringimos a seleção às linhas com produtos cujo nome iniciam com "B".

Ao conjunto de registros obtido aplicamos dois operadores: (Select) onde Selecionamos a coluna UnitPrice , obtendo assim uma coleção de de preços em seguida ; e usando o operador Average() , calculamos a média sobre a coleção de preços retornando um único valor.

Referências:

  • http://www.microsoft.com/brasil/msdn/Tecnologias/arquitetura/LINQ.mspx
  • LINQ - http://msdn2.microsoft.com/en-us/netframework/aa904594.aspx
  • 101 LINQ Samples
  • VB 2008 - Apresentando LINQ To SQL II
  • VB 2008 - Apresentando LINQ To SQL
  • VB 2008 - Usando LINQ To SQL
  • VB 2008 - Apresentando LINQ - Language Integrated Query
  • VB 2008 - LINQ To SQL - Criando Mestre-Detalhes
  • .NET - Aprendendo a sintaxe LINQ com o LINQPad
  • ASP .NET 2008 - LINQ To DataSet
  • ASP .NET 2008 - LINQ To SQL - operações básicas

  • José Carlos Macoratti