VB .NET - Camada de dados e TableAdapters em WIndows Forms


Se você digitar as palavras  DAL(Data Access layer) no Google vai encontrar centenas de referências. Há uns 5 anos atrás este tema estava quase que restrito uns poucos 'esclarecidos' que já vislumbravam os benefícios de se trabalhar em camadas.

Nota:  Veja também meu artigo sobre o assunto usando ASP.NET - ASP.NET 2.0 -  Criando uma camada de acesso a dados. 

Considero isso um bom sinal , afinal estamos todos evoluindo e procurando criar softwares de qualidade, com desempenho e de fácil manutenção. Um dia vamos chegar lá... (foi um piada...)

Bem, o meu assunto hoje aqui é mostrar como você, sim você mesmo, pode criar sua própria camada de acesso a dados ( a famosa DAL) usando o Visual Basic 2005 Express Edition e os seus novos recursos em aplicações Windows(leia-se TableAdapters).

O que é uma Camada de Acesso a Dados ?

É uma camada separada da camada da apresentação de dados que tem a função de efetuar a conexão com a fonte de dados e através de comandos SQL (neste nosso caso) obter os dados para apresentação.

Esta separação facilita a manutenção e a portabilidade da aplicação.

A camada de apresentação, quando deseja exibir algum dado, chama a camada de de negócios (ausente neste artigo) que chama a camada de acesso aos dados e solicita a informação.

A camada de apresentação não acessa dados nem a camada de dados faz apresentação. Cada uma cumpre o seu papel.

Obs: A camada de negócios não esta sendo criada por uma questão de simplificação e para facilitar o entendimento.

Em uma aplicação construída em camadas geralmente temos :

1- A camada de apresentação - Que é responsável por apresentar o resultado do processamento. Esta camada deve ser o menos dependente possível das demais camadas de forma que a sua manutenção não exija o conhecimento da lógica de negócio nem da camada de dados;

2- A camada da lógica de negócio -  Responsável pelas regras e processos de negócio da sua aplicação.

3- A camada de dados - Representam os dados da sua aplicação. Ela pode ser dividida em:

  1. A lógica da persistência;
  2. Os dados propriamente ditos;

Com isto em mente devemos ter:

Temos a seguir uma figura representando as camadas e a DAL(Data Access Layer)  que iremos criar:

Este artigo usará como exemplo um banco de dados criado no SQL Server 2005 Express. O banco de dados Cadastro.mdf e a tabela Clientes cuja estrutura é a seguinte :

Para saber como criar novos arquivos no SQL Server 2005 Express veja o artigo : .NET  2005 - Usando o SQL Server Management Studio

Exibindo a propriedades do banco de dados:

Um banco de dados local pode ser incluído como um arquivo no seu projeto. A primeira vez que você efetuar uma conexão com sua aplicação ao arquivo de banco de dados local você escolherá entre criar uma copia do banco de dados no seu projeto ou conectar-se ao banco de dados existente na sua localização original.

Se você escolher que deseja se conectar com o banco de dados existente , então a conexão é criada como se você estivesse se conectando a qualquer banco de dados remoto e o arquivo de banco de dados será deixado na sua localização original.

Se você escolher copiar o banco de dados para o seu projeto ,  o VB 2005 ou VS cria uma cópia do arquivo, e o inclui no seu projeto modificando a conexão de forma que ela agora aponta para o banco de dados no seu projeto.

Por padrão quando você constrói um projeto , o banco de dados é copiado da pasta raiz do projeto para a pasta de saida (bin). (Selecione  Show All Files para ver a pasta bin)
Este comportamento é devido a propriedade Copy to Output Directory do arquivo. A definição padrão para esta propriedade é Copy Always e isto significa que o banco de dados na pasta bin será copiado cada vez que você construir , debugar ou rodar sua aplicação ( ele é copiado da pasta raiz do projeto para pasta bin). (
Para evitar este efeito use Copy if newer.)

O arquivo de banco de dados na pasta raiz do seu projeto somente será alterado quando você editar o esquema ou os dados do banco de dados usando o Server Explorer/DataBase Explorer ou outra ferramenta Visual.

O relacionamento entre as duas versões do seu banco de dados é dependente da forma como você define a propriedade Copy to Output Directory.

Durante o desenvolvimento da aplicação, qualquer alteração feita nos dados (durante o tempo que você roda a sua aplicação) são feitas também no banco de dados da pasta bin. Assim por exemplo se você rodar a sua aplicação pressionando F5 você estará se conectando com o banco de dados na pasta bin.

Se você deixar a propriedade definida com o valor 'Copy always' uma nova cópia do banco de dados será copiada para a pasta bin cada vez que você executar sua aplicação e desta forma você não verá as alterações feitas no banco de dados e os dados parecerão que não estão sendo atualizados.

1- Criando um DataSet tipado e um TableAdapter

Como vemos na figura acima nossa proposta é criar uma camada de acesso a dados usando TableAdapter e DataTable de forma que todo o código de acesso aos dados esteja na DAL.

Inicie um novo projeto no Visual Basic 2005 Express Edition do tipo Windows Application com o nome de DALWindowsNet;

Clique com  o botão direito do mouse sobre o nome do projeto e selecione a opção Add -> Add  New Item

Na janela Add New Item selecione DataSet e informe o nome Cadastro.xsd

Um DataSet Tipado funciona como uma coleção fortemente tipada de dados e é composto de instâncias de DataTables fortemente tipadas onde cada uma é composta de instâncias de DataRow fortemente tipadas.

A seguir selecione o DataSet criado e abra a ToolBox. Na ToolBox arraste e solte o objeto tableAdapter para o formulário:

Ao fazer isto o assistente para criação de TableAdapter irá surgir. Selecione a conexão com a base Cadastro.mdf e clique em Next>;

Na próxima etapa salve a string de conexão e clique em Next>;

A seguir vamos definir o esquema do Datatable fortemente tipado e fornecer o primeiro método para acessar os dados via TableAdapter usado para preencher o DataSet tipado.

Para iniciar vamos definir a consulta SQL que deve indicar como queremos que o TableAdapter trate a consulta.

Clique no botão Next>

 

 

Podemos definir a consulta SQL digitando diretamente ou usando o Assistente construtor - Query Builder para montar o seu SQL e ver o resultado.

Vamos incluir um comando SELECT que retorne algumas das colunas da tabela Clientes.

SELECT Codigo, Nome, Endereco, Renda FROM Clientes

A seguir clique no botão Next>

Depois de criar a consulta clique no botão Advanced Options e serão exibidas as opções já marcadas pois estamos em uma aplicação Windows Forms. Se estivéssemos em um projeto  Web somente a primeira opção seria marcada por padrão.

Existe uma diferença básica entre as aplicações Windows e as aplicações Web:

As aplicações web geralmente atualizam registros individualmente enquanto que as aplicações Windows atualizam blocos de registros usando um DataSet.(Veja abaixo sobre os dois modelos de atualização usados pelo TableAdapter)(*)

O TableAdapter possui diferentes formas de atualização, prevendo justamente este comportamento diferente. Assim, em aplicações web ele pode usar os DbDirectMethods  e em aplicações Windows ele gera métodos de atualização que usam parâmetros recebidos (DataTable e DataSet) e atualizam todos os dados.

Na fase final temos dois padrões para preencher os dados:

- Fill a DataTable - cria um método que usa um DataTable como parâmetro e preenche-o baseado no resultado da consulta

- Return DataTable - cria e preenche o DataTable e retorna como o retorno do método.

Podemos e vamos usar ambos deixando as caixas de  opção marcadas. Vamos alterar o método GetData para Get.

Se marcarmos a última caixa de opção - , GenerateDBDirectMethods , serão criados os métodos : Insert(), Update() e Delete() para o TableAdapter. Se a caixa for desmarcada todas as atualizações deverão ser feitas através do método Update() do TableAdapter.(*)

Finalmente clique no botão FInish. (Finalmente...)

Neste momento já temos o DataSet tipado contendo  o DataTable (Cadastro.Clientes) e a classe DataAdapter (CadastroTableAdapters.ClientesTableAdapter) cujo método getClientes pode ser usado para obtermos os dados da tabela clientes e exibir na camada de apresentação.

Vamos usar o recurso na nossa aplicação Windows. Inclua no formulário padrão form1.vb um controle ListBox , um DataGridView e um botão de comando conforme figura abaixo:

Inclua o seguinte código no evento Click do botão de comando:
Private Sub Button1_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Dim clientesAdapter As CadastroTableAdapters.ClientesTableAdapter = New CadastroTableAdapters.ClientesTableAdapter

Dim clientes As Cadastro.ClientesDataTable

clientes = clientesAdapter.GetClientes

For Each cliente As Cadastro.ClientesRow In clientes

lstClientes.Items.Add(cliente.Codigo & vbTab & cliente.Nome & vbTab & cliente.Endereco & vbTab & cliente.Renda)

Next

dgvClientes.DataSource = clientes

End Sub

Executando o projeto iremos obter:

Conferindo o código acima vemos que realmente não escrevemos uma linha de código para acesso específico a dados. Não precisamos instanciar classes ado.net nem fazer referência a strings de conexão, consultas SQL ou procedimentos armazenados.

O responsável por todo o serviço foi o TableAdapter que fez o papel da nossa DAL.

 

 

2- Criando uma consulta parametrizada

Vamos agora estender um pouco as funcionalidades da nossa DAL incluindo um novo método no TableAdapter. Vamos incluir um método contendo um parâmetro de forma a poder selecionar um determinado cliente pelo seu Código - getClientesByCodigo(Codigo).

Volte a selecionar o DataSet e clique com o botão direito do mouse sobre a seção ClientesTableAdapter selecionando em seguida Add Query:

Como queremos obter todos os clientes para um determinado código iremos usar um SELECT que retorna linhas.

A seguir clique em Next>

Você pode montar a instrução SQL para obter os clientes pelo código usando o Query Builder ou digitando diretamente. Veja abaixo a instrução SQL criada.

No passo final podemos informar o nome dos métodos gerados conforme figura abaixo:

Após clicar no botão Finish se voltarmos ao nosso TableAdapter veremos os novos métodos gerados.

Vamos agora por o recurso para funcionar em nossa aplicação Windows, insira no formulário um controle ComboBox e uma Label conforme figura abaixo:

Para que os valores dos códigos sejam preenchidos pela combobox podemos selecionar o controle e na propriedade DataSource atribuir a tabela clientes conforme figura ao lado.

Após isso basta definir a propriedade DisplayMember como sendo o  codigo.

Agora para obter os clientes quando um código for selecionado na combobox inclua o seguinte código no evento SelectedIndexChanged :

Private Sub cboCodigo_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboCodigo.SelectedIndexChanged

If cboCodigo.Text = String.Empty Then Exit Sub

clientes = clientesAdapter.GetClientesByCodigo(CType(cboCodigo.Text, Integer))

dgvClientes.DataSource = clientes

End Sub

Executando o projeto teremos:

3- Incluindo , Excluindo e atualizando dados

Existem dois modelos usados para inserir , atualizar e deletar dados. O primeiro usa o modelo de acesso direto ao banco de dados e envolve a criação de métodos que quando invocados aplicam um comando INSERT, UPDATE e DELETE no banco de dados e que atuam em um único registro. Estes métodos são passados usando valores escalares (inteiros, strings, booleanos, etc.). Assim para excluir o cliente com código igual a 1 o método DELETE é passado com o parâmetro inteiro 1.(*)

Modelo de atualização por registro Modelo de atualização em lote

O TableAdapter usa por padrão a atualização em lote mas suporta também o modelo de acesso direto. No momento  em  que  nós selecionamos a opção "Generate Insert, Update, and Delete statements" a partir da opção Options Advanced quando da geração do  TableAdapter pelo Assistente, ele passou a conter o método Update() que implementa o modelo de atualização em lote.( Especificamente o TableAdapter contém o método Update() que pode passar o DataSet tipado, um DataTable tipado ou mais de um DataRows.) Neste momento se você deixar marcado a opção "GenerateDBDirectMethods"  os métodos insert(), update() e delete() também serão criados.

Ambos os modelos de atualização utilizam as propriedades  InsertCommand, UpdateCommand, e DeleteCommand do TableAdapter para aplicar os comandos INSERT, UPDATE e DELETE ao banco de dados. Você pode dar uma olhada nestas propriedades na janela de propriedades e pode também alterá-las selecionando a propriedade CommandText e clicando no botão a direita para abrir a janela Query Builder.

As propriedades InsertCommand, UpdateCommand, e DeleteCommand do TableAdpater

 

Para usar os métodos de atualização individuais gerados por GenerateDBDirectMethods basta invocar o método com o parâmetro apropriado. Para a nossa tabela clientes e para o nosso TableAdapter  podemos ter:

Dim clientesAdapter As New CadastroTableAdapters.ClientesTableAdapter

' Deleta o cliente com código igual a 1
clientesAdapter.Delete(1)

' Atualiza o endereco cliente de codigo 1
clientesAdapter.Update("Macoratti", "Rua Projetada", 120, 1, "Macoratti", "Rua Teste, 100", 120, 1)

' Inclui um novo cliente
clientesAdapter.Insert("Macoratti", "Rua teste,100", 90)

Para atualizar a atualização em lote, como exemplo podemos usar o seguinte trecho de código que atualiza a renda dos clientes com código superior a 2;

Dim clientesAdapter As New CadastroTableAdapters.ClientesTableAdapter

Dim clientes As Cadastro.ClientesDataTable = clientesAdapter.GetClientes
 

'atualiza a renda dos clientes com codigo superior a 2

For Each cliente As Cadastro.ClientesRow In clientes

    If Not cliente.Codigo > 2 Then

         cliente.Renda += 10

     End If

Next


'atualização em lote dos dados dos clientes

clientesAdapter.Update(clientes)

Obs: Estes métodos poderiam ser chamados a partir de eventos de botões ou de rotinas de sua aplicação Windows.

4- Personalizando as inclusões , exclusões e atualizações

Se você der uma espiada nos métodos insert(), update() e delete() de acesso direto aos dados criados no TableAdapter pelo assistente vai notar que eles são complexos, principalmente para tabelas com muitas colunas. Será que não podemos gerar os nosso próprios métodos para realizar tais tarefas de uma maneira mais simples ?

 Sim, podemos usar o assistente para incluir métodos que realizam tais operações.  Vamos criar um novo método para incluir um novo registro na nossa tabela Clientes.

Para fazer isto clique com o botão direito do mouse sobre o TableAdapter e selecione a opção Add Query e a seguir na janela do assistente selecione a opção Use SQL Statements

 

A seguir clique em Next>

Na próxima janela - Choose a Query Type - selecione a opção - INSERT ( adds a new row to a table) e clique no botão Next>

A próxima tela exibre o comandText insertCommand que inclui um novo registro na tabela clientes. Ajuste a instrução exibida com a cláusula SELECT SCOPE IDENTITY com isto estaremos retornando o último valor do código (IDENTITY) incluído na tabela.


No SQL Server após você realizar uma operação de inclusão de dados via INSERT , SELECT INTO ou uma cópia em massa (BULKY COPY) em uma tabela com uma coluna IDENTITY, a variável global @@IDENTITY conterá o último valor da identidade que é gerado pela instrução. (Se nenhuma coluna for sensibilidade será retornado NULL). Se múltiplas linhas tiverem sido incluídas , gerando múltiplos valores identidade(IDENTITY) o último será retornado.

Para obter o último valor identidade gerado nestes casos  podemos usar as funções @@IDENTITY, SCOPE_IDENTITY, e IDENT_CURRENT.

IDENT_CURRENT  - Retorna o último valor identidade gerado para uma tabela específica em qualquer sessão e em qualquer escopo.

@@IDENTITY
 Retorna o último valor identidade gerado para qualquer tabela na sessão atual, através de todos os escopos.(valor inserido na coluna com propriedade IDENTITY).

SCOPE_IDENTITY - Retorna o último valor identidade gerado para qualquer tabela na sessão atual e no escopo atual.

Sendo que @@IDENTITY e SCOPE_IDENTITY retornam o último valor identidade gerado em qualquer tabela na sessão atual; porém  SCOPE_IDENTITY retorna o valor somente para o escopo atual.

Clique em Next> e informe o nome da função a ser gerada como sendo : incluirCliente.

Na etapa final você verá a tela de resultado do assistente conforme a abaixo e após clicar no botão Finish verá o TableAdapter exibindo o novo método - incluirCliente :

Como último ajuste você pode alterar a propriedade ExecuteMode da Query criada. O padrão é NonQuery que retorna o valor das linhas afetadas pela execução da query. Para retornar o valor da consulta altere para Scalar.

Para usar o novo recurso inclua um novo formulário através do menu Project -> Add Windows Forms . Será incluído o formulário form2.vb no projeto.

Insira o formulário 3 Labels e 3 caixas de Textos dois controles Button conforme figura abaixo:

Agora inclua o seguinte código no formulário:

Dim clientesAdapter As CadastroTableAdapters.ClientesTableAdapter = New CadastroTableAdapters.ClientesTableAdapter
 

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

    Me.Close()

End Sub
 

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Try

     clientesAdapter.incluirCliente(txtNome.Text, txtEndereco.Text, CType(txtRenda.Text, Decimal))

     MsgBox("Registro incluido com sucesso")

Catch ex As Exception

    MsgBox(ex.Message)

End Try

End Sub

Pronto ! com poucas linhas de código acabamos de usar novo método inserirCliente para incluir um novo registro na base de dados.

Nota: Para tornar o exemplo bem simples não efetuei a validação dos dados.

Encerro aqui esta pequena introdução a criação de uma camada de acesso a dados (DAL) usando TableAdapter para aplicações WIndows Forms.

Se você percebeu a tarefa não exige grandes conhecimentos nem é muito trabalhosa. Podemos aprofundar mais o assunto mostrando como criar regras de negócio e efetuar a sua integração entre as camadas e também com estender a funcionalidade do TableAdapter.

Mas isto é assunto para outro artigo. Até lá...

Pegue o projeto exemplo aqui : DALNet.zip

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti