VB .NET - Acessando um banco de dados Access (CRUD) - II


Na primeira parte deste artigo eu crie uma aplicação VB .NET para realizar a manutenção em uma tabela Alunos do banco de dados Escola.mdb.

Assim criamos o banco de dados Escola.mdb e a tabela Alunos e a seguir criamos o projeto no VB .NET 2010 Express onde definimos as operações para incluir, alterar, excluir e consultar registros na tabela Alunos.

Concluímos e testamos o projeto e esta tudo funcionando sem problemas.

O que vamos então fazer neste artigo ?

Embora nossa aplicação esteja funcional ele pode ser melhorada para ser mais fácil de manter e de estender.

Como assim ???

Quando criamos nossa aplicação definimos na camada de apresentação, no caso o formulário form1.vb, todas as operações nos eventos dos botões de comando.

Assim usamos o código para acessar e para apresentar os dados em um único lugar: a camada de apresentação.

Se você analisar o código usado irá notar que usamos referências aos namespaces System.Data e System.Data.Oledb no formulário e que no código usado criamos instâncias de objetos ADO .NET como Connection, Command, DataTable, DataAdatper também no formulário. Além disso definimos os comandos SQL para selecionar, incluir, alterar e excluir também no formulário.

Temos também código duplicado no formulário, principalmente na definição da string de conexão, que esta sendo definida em cada evento Click dos botões Novo,Deletar, Alterar e Procurar.

Dessa forma nosso formulário além de código duplicado também possui mesclado o código de apresentação e o código de acesso a dados e isso não é uma boa prática.

Por quê ?

Porque qualquer alteração relacionada com o acesso aos dados implicará em alterar também a camada de apresentação. A criação de um novo campo, por exemplo, faria com que o formulário form1.vb fosse todo alterado pois nele esta contido toda a lógica de acesso aos dados.

Se a string e conexão for alterada também teremos que alterar o código em todo o formulário.

Vamos então separar as responsabilidades e remover do formulário todo o código e referência relacionada com o acesso aos dados criando uma camada de acesso a dados que será responsável conhecer, acessar, e retornar as informações dos dados.

Nosso formulário form1.vb deverá unicamente chamar a camada de acesso a dados e apresentar o resultado ao usuário.

Além disso vamos definir a string de conexão em um único lugar na aplicação que será o arquivo de configuração App.Config. Para obter a string de conexão iremos acessar e recuperar a string de conexão deste arquivo. Assim se houver qualquer mudança na string de conexão teremos que alterar somente em único local o seu valor.

Criando a infraestrutura da camada de acesso aos dados

Abra o projeto original CadastroAlunosAccess onde teremos apenas o formulário form1.vb. Vamos criar a infraestrutura para podermos criar a nossa camada de acesso aos dados

1- Referenciando o namespace System.Configuration

No menu Project, clique em Add Reference e na janela Add Reference clique na guia .NET e selecione System.Configuration e clique em OK;

2- Criando o arquivo de configuração App.Config

No menu Project clique em Add New Item e selecione o template Application Configuration FIle aceitando o nome padrão app.config e clique em Add;

3- Definindo a string e conexão no arquivo app.config

Abra o arquivo app.config criado e inclua o código destacado em azul abaixo neste arquivo:

<?xml version="1.0"?>
<configuration>

  <connectionStrings>
       <add name="ConexaoEscolaMDB" providerName="System.Data.OleDb" connectionString="Provider=Microsoft.Jet.OleDb.4.0; Data Source=C:\dados\Escola.mdb"/>
   </connectionStrings>


<system.diagnostics>
  ........
 

Definimos no arquivo a string de conexão com o banco de dados identificada pelo nome ConexaoEscolaMDB.

4- Criando a classe Aluno

No menu Project, Clique em Add Class, informe o nome Aluno.vb e clique no botão Add;

A seguir defina o código abaixo na classe Aluno:

Public Class Aluno

   Public Property Codigo As Integer
   Public
Property Nome As String
   Public
Property Imagem As Image

End Class

A classe Aluno representa um aluno e possui 3 propriedades que possuem nomes idênticos aos campos da tabela Alunos.

Usamos essa classe para realizar a comunicação entre as camadas de acesso a dados e a camada de apresentação. Podemos dizer que essa classe é um DTO - Data Transfer Object.

Criando a camada de apresentação

Volte ao menu Project, clique em Add Class e informe o nome AcessoDados.vb clicando a seguir no botão Add para criar a classe AcessoDados;

Defina os seguintes namespaces neste arquivo:

Imports System.Data
Imports
System.Data.OleDb
Imports
System.Configuration

E a seguir, logo após a declaração da classe vamos definir os objetos ADO .NET:

Dim Da As New OleDbDataAdapter
Dim Dt As DataTable
Dim Cmd As New OleDbCommand
Dim cn As New OleDb.OleDbConnection

Agora podemos iniciar a criação dos métodos da classe AcessoDados que irão acessar e persistir os dados na tabela Alunos.

1- Método  getConexaoDB() - obtém a string de conexão do arquivo app.config, abre e retorna uma conexão do tipo OledbConnection;

Public Function getConexaoDB() As OleDbConnection
        Try
            cn.ConnectionString = ConfigurationManager.ConnectionStrings("ConexaoEscolaMDB").ConnectionString
            cn.Open()
            Return cn
        Catch ex As Exception
            Throw ex
        End Try
    End Function

2- Método  closeConexaoDB() -  Fecha uma conexão aberta

Public Sub closeConexaoDB(ByVal cn As OleDbConnection)
        Try
            If cn.State = ConnectionState.Open Then
                cn.Close()
            End If
        Catch ex As Exception
            Throw ex
        End Try
    End Sub

3- Método incluirDados() - Inclui um novo aluno na tabela alunos usando o comando SQL INSERT INTO;


    
Public Sub incluirDados(ByVal nome As String, ByVal arrImage() As Byte, ByVal strImage As String)
        Try
            cn = getConexaoDB()
            Cmd.Connection = cn
            Cmd.CommandText = "INSERT INTO Alunos(nome, imagem) " & _
                                             "VALUES( '" & nome & "'," & strImage & ")"
            If strImage = "?" Then
                Cmd.Parameters.Add(strImage, OleDb.OleDbType.Binary).Value = arrImage
            End If
            Cmd.ExecuteNonQuery()
        Catch ex As Exception
            Throw ex
        Finally
            closeConexaoDB(cn)
        End Try
    End Sub

4- Método procurarDados() - seleciona um aluno pelo código e retorna um objeto Aluno.

Public Function procurarDados(ByVal codigo As Integer) As Aluno
        Dim aluno As New Aluno
        Try
            cn = getConexaoDB()
            Dim da As New OleDb.OleDbDataAdapter("SELECT * FROM Alunos " & " WHERE codigo=" & codigo, cn)
            Dim dt As New DataTable
            Dim arrImage() As Byte = Nothing
            Dim myMS As New IO.MemoryStream
            da.Fill(dt)
            If dt.Rows.Count > 0 Then
                aluno.Codigo = dt.Rows(0).Item("codigo")
                aluno.Nome = dt.Rows(0).Item("nome") & ""
                If Not IsDBNull(dt.Rows(0).Item("imagem")) Then
                    arrImage = dt.Rows(0).Item("imagem")
                    For Each ar As Byte In arrImage
                        myMS.WriteByte(ar)
                    Next
                    aluno.Imagem = System.Drawing.Image.FromStream(myMS)
                Else
                    aluno.Imagem = Nothing
                End If
            Else
                aluno = Nothing
            End If
        Catch ex As Exception
            Throw ex
        Finally
            closeConexaoDB(cn)
        End Try
        Return Aluno
    End Function

5- Método carregaDados() - Seleciona todos os alunos e retorna um DataTable.

 Public Function carregaDados() As DataTable
        Dt = New DataTable
        cn = getConexaoDB()
        Try
            With Cmd
                .CommandType = CommandType.Text
                .CommandText = "SELECT * from Alunos"
                .Connection = cn
            End With
            With Da
                .SelectCommand = Cmd
                .Fill(Dt)
            End With
        Catch ex As Exception
            Dt = Nothing
        Finally
            closeConexaoDB(cn)
        End Try
        Return Dt
    End Function

6-  Método deletarRegistro() - exclui um aluno pelo código da tabela Alunos.

 Public Sub deletarRegistro(ByVal codigo As Integer)
        Try
            cn = getConexaoDB()
            Dim myCmd As New OleDb.OleDbCommand
            myCmd.Connection = cn
            myCmd.CommandText = "DELETE FROM Alunos WHERE codigo = " & codigo
            myCmd.ExecuteNonQuery()
        Catch ex As Exception
            Throw ex
        Finally
            closeConexaoDB(cn)
        End Try
    End Sub

7- Método atualizarDados() - Atualiza os dados de um aluno selecionado pelo código.

Public Sub atualizarDados(ByVal nome As String, strImagem As String, codigo As Integer, arrImagem() As Byte)
        Try
            cn = getConexaoDB()
            Dim myCmd As New OleDb.OleDbCommand
            myCmd.Connection = cn
            myCmd.CommandText = "Update Alunos SET nome = '" & nome & "'," & "imagem = " & strImagem & " WHERE codigo =" & codigo
            If strImagem = "?" Then
                myCmd.Parameters.Add(strImagem, OleDb.OleDbType.Binary).Value = arrImagem
            End If
            myCmd.ExecuteNonQuery()
        Catch ex As Exception
            Throw ex
        Finally
            closeConexaoDB(cn)
        End Try
    End Sub

Note que em todos os métodos usamos o tratamento de erros Try/Catch/Finally onde o código do bloco Finally sempre será executado.

Alterando a camada de apresentação

Agora que temos uma camada de acesso aos dados criada e pronta para o uso vamos alterar o código do formulário form1.vb removendo toda a referência e código de acesso aos dados.

Vou mostrar a esquerda o código anterior usado e à esquerda no novo código que usa a nossa camada de acesso aos dados.

Remova as referências a System.Data e System.Data.OleDb do formulário.

Vamos criar uma instância da classe AcessoDados logo após a declaração do formulário:

Dim acc As New AcessoDados

No evento Load do formulário que ocorre quando a aplicação for aberta vamos continuar a usar o código que chama a rotina CarregaDados();

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        CarregaDados()
    End Sub

A rotina CarregaDados() vai acessar o banco de dados e selecionar todas as informações existentes e exibi-las no controle DataGridView no formulário form1;

Abaixo vemos à esquerda como era a rotina e à direita como ficou o novo código:

Note que estamos usando o objeto acc que é uma instância da classe AcessoDados e estamos chamando o método carregaDados()

 Private Sub CarregaDados()

        Dim cn As New OleDb.OleDbConnection
        cn.ConnectionString = "Provider=Microsoft.Jet.OleDb.4.0; Data Source=c:\dados\Escola.mdb"
        cn.Open()

        Try
            With Cmd
                .CommandType = CommandType.Text
                .CommandText = "SELECT * from Alunos"
                .Connection = cn
            End With

            With Da
                .SelectCommand = Cmd
                Dt = New DataTable
                .Fill(Dt)
                dgvAlunos.DataSource = Dt
            End With

        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub
Try

    dgvAlunos.DataSource = acc.carregaDados()

Catch ex As Exception

     MsgBox(ex.Message)

End Try

 

A seguir temos o código do evento Click do botão Incluir que acessa o banco de dados, abre uma conexão e inclui um novo registro na tabela Alunos usando uma instrução SQL INSERT INTO;

Á direita o código antigo e a esquerda o novo código onde chamamos o método incluirDados() da classe AcessoDados:

Private Sub btnIncluir_Click(sender As System.Object, e As System.EventArgs) Handles btnIncluir.Click
        Dim cn As New OleDb.OleDbConnection
        cn.ConnectionString = "Provider=Microsoft.Jet.OleDb.4.0; Data Source=c:\dados\Escola.mdb"
        cn.Open()

        Dim arrImagem() As Byte
        Dim strImagem As String
        Dim ms As New IO.MemoryStream

        If txtNome.Text = String.Empty Then
            MsgBox("Informe o nome do aluno")
            txtNome.Focus()
            Return
        End If
        '
        If Not IsNothing(Me.picFoto.Image) Then
            Me.picFoto.Image.Save(ms, Me.picFoto.Image.RawFormat)
            arrImagem = ms.GetBuffer
            strImagem = "?"
        Else
            arrImagem = Nothing
            strImagem = "NULL"
        End If

        Dim myCmd As New OleDb.OleDbCommand
        myCmd.Connection = cn
        myCmd.CommandText = "INSERT INTO Alunos(nome, imagem) " & _
                                               " VALUES( '" & Me.txtNome.Text & "'," & strImagem & ")"

        If strImagem = "?" Then
            myCmd.Parameters.Add(strImagem, OleDb.OleDbType.Binary).Value = arrImagem
        End If

        myCmd.ExecuteNonQuery()
        MsgBox("Dados Salvos com sucesso!")

        cn.Close()
        CarregaDados()
    End Sub
 Private Sub btnIncluir_Click(sender As System.Object, e As System.EventArgs) 
Handles btnIncluir.Click
        Dim arrImage() As Byte
        Dim strImage As String
        Dim myMs As New IO.MemoryStream
        If txtNome.Text = String.Empty Then
            MsgBox("Informe o nome do aluno")
            txtNome.Focus()
            Return
        End If
        If Not IsNothing(Me.picFoto.Image) Then
            Me.picFoto.Image.Save(myMs, Me.picFoto.Image.RawFormat)
            arrImage = myMs.GetBuffer
            strImage = "?"
        Else
            arrImage = Nothing
            strImage = "NULL"
        End If
        Try
           acc.incluirDados(txtNome.Text, arrImage, strImage)
            MsgBox("Dados Salvos com sucesso!")
        Catch ex As Exception
            MsgBox("Erro ao incluir dados!")
        End Try
        CarregaDados()
    End Sub

 

No evento Click do botão de comando Procurar o código abaixo chama a rotina Procurar();

Aqui não houve alteração.

 Private Sub btnProcurar_Click(sender As System.Object, e As System.EventArgs) Handles btnProcurar.Click
        If txtCodigo.Text = String.Empty Then
            MsgBox("Informe o codigo do aluno")
        Else
            Procurar(Me.txtCodigo.Text)
        End If
    End Sub

A rotina Procurar() recebe o código do aluno, abre a conexão com o banco de dados e seleciona o aluno pelo seu código preenchendo um DataTable e populando os controles do formulário com os dados obtidos;

O novo código à direita chama o método procurarDados() e preenche um objeto aluno exibindo os dados no formulário:

 Private Sub Procurar(ByVal codigo As Integer)
        Dim cn As New OleDb.OleDbConnection
        cn.ConnectionString = "Provider=Microsoft.Jet.OleDb.4.0; Data Source=c:\dados\Escola.mdb"
        cn.Open()

        Dim arrImagem() As Byte
        Dim ms As New IO.MemoryStream
        Dim da As New OleDb.OleDbDataAdapter("SELECT * FROM Alunos " & _
                                                                        " WHERE codigo=" & codigo, cn)
        Dim dt As New DataTable
        da.Fill(dt)

        If dt.Rows.Count > 0 Then
            Me.txtCodigo.Text = dt.Rows(0).Item("codigo")
            Me.txtNome.Text = dt.Rows(0).Item("nome") & ""
            If Not IsDBNull(dt.Rows(0).Item("imagem")) Then
                arrImagem = dt.Rows(0).Item("imagem")
                For Each ar As Byte In arrImagem
                    ms.WriteByte(ar)
                Next
                '
                Me.picFoto.Image = System.Drawing.Image.FromStream(ms)
            Else
                Me.picFoto.Image = _ 
System.Drawing.Image.FromFile(Application.StartupPath & "/semfoto.jpg")
            End If
            Me.btnIncluir.Enabled = False
        Else
            MsgBox("Registro não localizado")
        End If

        cn.Close()

    End Sub
Private Sub Procurar(ByVal codigo As Integer)
        Dim arrImage() As Byte = Nothing
        Dim myMS As New IO.MemoryStream
        Dim aluno1 As New Aluno
        aluno1 = acc.procurarDados(codigo)
        If Not IsNothing(aluno1) Then
            Me.txtCodigo.Text = aluno1.Codigo
            Me.txtNome.Text = aluno1.Nome
            If Not IsDBNull(aluno1.Imagem) Then
                Me.picFoto.Image = aluno1.Imagem
            Else
                Me.picFoto.Image = _ 
System.Drawing.Image.FromFile(Application.StartupPath & "/semfoto.jpg")
            End If
            Me.btnIncluir.Enabled = False
        Else
            MsgBox("Registro não localizado")
        End If
    End Sub

 

No evento Click do controle LinkLabel o código abre uma caixa de diálogo para que uma imagem seja selecionada e exibida no controle Image;

Aqui não houve alteração no código:

 Private Sub lnkProcurar_LinkClicked(sender As System.Object, e As System.Windows.Forms.LinkLabelLinkClickedEventArgs) Handles lnkProcurar.LinkClicked
        If Me.ofdImagem.ShowDialog = 1 Then
            Me.picFoto.Image = System.Drawing.Image.FromFile(Me.ofdImagem.FileName)
        Else
            Me.picFoto.Image = System.Drawing.Image.FromFile(Application.StartupPath & "/semfoto.jpg")
        End If
    End Sub

No evento CellClick do controle DataGridView quando o usuário clicar em um célula do controle iremos selecionar o código da célula clicada e chamar a rotina Procurar passando este código;

Aqui não houve alteração no código:

Private Sub dgvAlunos_CellClick(sender As System.Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvAlunos.CellClick
        Try
            'Dim codigo As Integer = dgvAlunos.Rows(e.RowIndex).Cells(e.ColumnIndex).Value()
            Dim codigo As Integer = dgvAlunos.Rows(e.RowIndex).Cells(0).Value()
            Procurar(codigo)
        Catch ex As Exception
            MsgBox("Seleção Inválida. Clique em uma célula com dados.")
        End Try
    End Sub

No evento Click do botão Limpar limpamos os controles do formulário;

Aqui não houve alteração no código:

 Private Sub btnLimpar_Click(sender As System.Object, e As System.EventArgs) Handles btnLimpar.Click
        Me.txtCodigo.Text = ""
        Me.txtNome.Text = ""
        Me.picFoto.Image = Nothing
        Me.txtCodigo.Focus()
        Me.btnIncluir.Enabled = True
    End Sub

O evento Click do botão Deletar será solicitada uma confirmação para excluir o registro selecionado; a seguir será aberta uma conexão com o banco de dados e o registro selecionado será deletado usando a instrução SQL DELETE FROM;

O novo código à direita chama o método deletarRegistro():

Private Sub btnDeletar_Click(sender As System.Object, e As System.EventArgs) Handles btnDeletar.Click

        Dim resultado As DialogResult = MessageBox.Show("Confirma a exclusão deste registro ?", _
            "Excluir", MessageBoxButtons.YesNo, MessageBoxIcon.Question)

        If resultado = vbYes Then
            Dim cn As New OleDb.OleDbConnection
            cn.ConnectionString = "Provider=Microsoft.Jet.OleDb.4.0; Data Source=c:\dados\Escola.mdb"
            cn.Open()

            If txtCodigo.Text = String.Empty Then
                MsgBox("Informe o codigo do aluno")
                txtNome.Focus()
                Return
            End If

            Dim myCmd As New OleDb.OleDbCommand
            myCmd.Connection = cn
            myCmd.CommandText = "DELETE FROM Alunos WHERE codigo = " & txtCodigo.Text

            myCmd.ExecuteNonQuery()
            MsgBox("Dados excluídos com sucesso!")

            cn.Close()
            CarregaDados()
        End If
    End Sub
Private Sub btnDeletar_Click(sender As System.Object, e As System.EventArgs)
 Handles btnDeletar.Click
        Dim resultado As DialogResult = MessageBox.Show("Confirma a exclusão deste registro ?", _
            "Excluir", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
        If resultado = vbYes Then
            Dim codigo As Integer
            If txtCodigo.Text = String.Empty Then
                MsgBox("Informe o codigo do aluno")
                txtNome.Focus()
                Return
            Else
                codigo = Convert.ToInt32(txtCodigo.Text)
            End If
            Try
                acc.deletarRegistro(codigo)
                MsgBox("Dados excluídos com sucesso!")
            Catch ex As Exception
                MsgBox(ex.Message)
            End Try
            CarregaDados()
        End If
    End Sub

 

No evento Click do botão Alterar abrimos uma conexão com o banco de dados e alteramos as informações do registro selecionado usando a instrução SQL UPDATE/SET;

Private Sub btnAlterar_Click(sender As System.Object, e As System.EventArgs) Handles btnAlterar.Click
        Dim cn As New OleDb.OleDbConnection
        cn.ConnectionString = "Provider=Microsoft.Jet.OleDb.4.0; Data Source=c:\dados\Escola.mdb"
        cn.Open()

        Dim arrImagem() As Byte
        Dim strImagem As String
        Dim ms As New IO.MemoryStream

        If txtNome.Text = String.Empty Then
            MsgBox("Informe o nome do aluno")
            txtNome.Focus()
            Return
        End If
        '
        If Not IsNothing(Me.picFoto.Image) Then
            Me.picFoto.Image.Save(ms, Me.picFoto.Image.RawFormat)
            arrImagem = ms.GetBuffer
            strImagem = "?"
        Else
            arrImagem = Nothing
            strImagem = "NULL"
        End If

        Dim myCmd As New OleDb.OleDbCommand
        myCmd.Connection = cn
        myCmd.CommandText = "Update Alunos SET nome = '" & txtNome.Text & "'," & "imagem = " & strImagem & " WHERE codigo =" & txtCodigo.Text

        If strImagem = "?" Then
            myCmd.Parameters.Add(strImagem, OleDb.OleDbType.Binary).Value = arrImagem
        End If

        myCmd.ExecuteNonQuery()
        MsgBox("Dados Alterados com sucesso!")

        cn.Close()
        CarregaDados()
    End Sub
Código anterior

Abaixo o novo código  que chama o método atualizarDados():

Private Sub btnAlterar_Click(sender As System.Object, e As System.EventArgs) Handles btnAlterar.Click
        Dim arrImage() As Byte
        Dim strImage As String
        Dim myMs As New IO.MemoryStream
        If txtNome.Text = String.Empty Then
            MsgBox("Informe o nome do aluno")
            txtNome.Focus()
            Return
        End If
        If Not IsNothing(Me.picFoto.Image) Then
            Me.picFoto.Image.Save(myMs, Me.picFoto.Image.RawFormat)
            arrImage = myMs.GetBuffer
            strImage = "?"
        Else
            arrImage = Nothing
            strImage = "NULL"
        End If
        Try
            acc.atualizarDados(txtNome.Text, strImage, txtCodigo.Text, arrImage)
            MsgBox("Dados Alterados com sucesso!")
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
        CarregaDados()
    End Sub

 

No evento Validating da caixa de texto txtNome usamos o controle ErrorProvider para validar o campo txtNome;

Aqui não houve alteração de código:

 Private Sub txtNome_Validating(sender As System.Object, e As System.ComponentModel.CancelEventArgs) Handles txtNome.Validating
        If (txtNome.Text.Trim().Length = 0) Then
            erro.SetError(txtNome, "Informe o nome do aluno")
        Else
            erro.SetError(txtNome, "")
        End If
    End Sub

Executando a solução teremos a exibição do formulário conforme mostra a figura abaixo obtendo assim o mesmo resultado final:

Dessa forma criamos uma camada de acesso a dados e aplicamos uma das principais regras das boas práticas : a separação de responsabilidades.

Podemos melhorar ainda mais nossa aplicação usando um ORM como o Entity Framework para remover todas as referências aos objetos ADO .NET e os comandos SQL e trabalhar somente com objetos.

Pegue o projeto completo aqui: CadastroAlunosAccess_CamadaAcessoDados.zip

Aguarde que em outro artigo eu vou mostrar como usar o Entity Framework e realizar o mapeamento ORM abstraindo assim os objetos ADO .NET e os comandos SQL.

Heb 2:17 Pelo que convinha que em tudo fosse feito semelhante a seus irmãos, para se tornar um sumo sacerdote misericordioso e fiel nas coisas concernentes a Deus, a fim de fazer propiciação pelos pecados do povo.

Heb 2:18 Porque naquilo que ele mesmo, sendo tentado, padeceu, pode socorrer aos que são tentados.

 

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti