VB.NET - Criando uma aplicação em 3 camadas (MVC)


No artigo - Padrões de Projeto : O modelo MVC - Model View  Controller - falei sobre os padrões de projeto , mais especificamente do modelo MVC. Vou mostrar neste artigo um exemplo prático de como podemos criar uma aplicação em 3 camadas , bem próxima do modelo MVC , usando o VB.NET.

Vou criar um projeto do tipo VB.NET com um formulário e um datagrid usando o acesso a dados via DataSet para criar uma aplicação em 3 camadas que permita acessar , alterar e atualizar os dados usando o DataGrid e refletir isto em um DataSet que representa a tabela Customer do banco de dados Northwind.mdb.

O código irá mostrar como enviar novas linhas (registros) , alterar linhas e excluir linhas usando uma camada de serviços que atualiza o banco de dados usando um DataSet. Iremos dividir logicamente nosso projeto em 3 partes :

  1. Camada de apresentação - Contém o código dos formulários Windows - Windows Forms - como o usuário e os serviços de negócios.
  2. Camada de negócios :  Contém o código para os serviços de negócios
  3. Camada de dados : O banco de dados Northwind.mdb - tabela Customer.

A aplicação que irei desenvolver pode ser dividido em dois projetos distintos : Um projeto baseado em Windows-Forms para apresentar os dados para o usuário e um projeto do tipo Class Library para gerenciar as regras de negócios e fazer a chamada para manipular os dados. (Os serviços de negócios ficariam responsáveis pelas tarefas de retornar e atualizar os dados na fonte de dados.)

A camada de apresentação -  Windows Forms

Vamos começar com a camada de apresentação. Abaixo temos o formulário do projeto com os seguintes componentes:

- 1 DataGrid - grdData

- 3 Buttons - Button1 , Button2  e Button3

O componente DataGrid irá exibir os dados ao usuário e os botões irão ter as seguintes funcionalidades :

  • Carregar Dados - carrega o DataSet com dados da tabela e exibe
  • Salvar Dados - Salva as alterações no Dataset
  • Sair - sai da aplicação.

Por que estou usando um DataGrid ? Bem...

O DataGrid é um controle de Servidor que pode ser facilmente manipulado para renderizar um conjunto de registros pela vinculação com  um DataSet.

Você deve estar meio desconfiado e deve também estar duvidando que é possível criar uma aplicação 3 camadas da forma com eu estou fazendo. Com certeza ao ler que eu vou vincular o DataSet ao DataGrid você pode até pensar que a vinculação será feita feita como nos velhos tempos do VB6.

Calma !!! No mundo .NET vincular tem um sentido muito diferente. Quando um DataSet é vinculado a um DataGrid , os dados do DataSet são copiados para o DataGrid. As alterações feitas nos dados do DataGrid são então repassadas para o DataSet para refletir as  mundaças. Qual a diferença ???  Vou explicar ...

Quando você usava o VB6 e a ADO (lembra ???) as técnicas de vinculação de dados envolviam vincular os dados do Grid diretamente a um controle Data e dai para o banco de dados. Neste esquema a conexão com o banco de dados fica aberta o que limita muito nossa aplicação pois você tem a camada de apresentação diretamente ligada com a camada de dados.

Com o VB .NET temos o grid ainda vinculado ao DataSet , mas o DataSet não tem uma conexão com o banco de dados. Na verdade o DataSet nem mesmo sabe ou se importa em saber de onde os dados estão vindo. O DataSet pode ser preenchido com dados a partir de uma variedade de fonte de dados e até mesmo se preenchido manualmente sem fonte de dados alguma. Ai que esta a diferença...

Quando o usuário altera os dados no DataGrid do formulário, as mudanças são enviadas ao DataSet de forma automática. Qualquer nova linha de registro incluída é também incluída no DataSet e qualquer linha alterada é atualizada no DataSet. Linhas excluídas do DataGrid são removidas do DataSet (elas são marcadas como excluidas).

O DataSet trata todas as mudanças nos dados  armazenando o valor original e atual de cada linha e coluna. Assim , se um DataGrid contiver 1000 linhas de dados e o usuário alterar 3 linhas, incluir 1 nova linha e excluir outras 2 linhas, as linhas afetadas podem ser extraíadas do DataSet vinculado para outro DataSet de maneira que o novo DataSet irá conter somente estas 6 linhas alteradas.

A seguir o código da classe frmMain :  Aqui temos a classe frmMain onde a variável m_oDS representa o DataSet. 

Public Class frmMain

Inherits System.Windows.Forms.Form

'//----------------------------------------------------------------------------------------------------

'// Declara variável de class

'//----------------------------------------------------------------------------------------------------

Private m_oDS As DataSet

 

'//----------------------------------------------------------------------------------------------------

'// Retorna e carrega os dados para o formulário

'//----------------------------------------------------------------------------------------------------

Private Sub LoadData()

   Dim oCustomer As BusinessServices.Customer = New BusinessServices.Customer()

   grdData.DataBindings.Clear()

   m_oDS = oCustomer.GetData()

   grdData.DataSource = m_oDS.Tables("Customer")

   oCustomer = Nothing

End Sub

'//------------------------------------------------------------------------------------------------------------

'// Envia os dados alterados para os serviços de negócios

'//------------------------------------------------------------------------------------------------------------

Private Sub SaveData()

 

Dim lRetVal As Long

Dim oCustomer As BusinessServices.Customer = New BusinessServices.Customer()

Dim oDS_Delta As DataSet

Dim sMsg As String

'//--- Verifica se houve alterações

   If m_oDS Is Nothing Then Exit Sub

      If Not m_oDS.HasChanges() Then Exit Sub

       '//--- Trata todas as mudanças

       oDS_Alterados = m_oDS.GetChanges()

       sMsg = "Esta certo que quer salvar   " & oDS_Alterados.Tables(0).Rows.Count() & "  linhas da fonte de dados ? "

       lRetVal = MsgBox(sMsg, Microsoft.VisualBasic.MsgBoxStyle.Question + Microsoft.VisualBasic.MsgBoxStyle.YesNo, "Salva Dados")

       Select Case lRetVal

           Case vbYes

              Try

                 '//--- Salva todas as alterações

                 sMsg = oCustomer.SaveData(oDS_Alterados)

                 LoadData()

             Catch e As Exception

                 sMsg = "Erro ao salvar dados." & vbCrLf & vbCrLf & e.Message.ToString()

             Finally

                  MsgBox(sMsg, Microsoft.VisualBasic.MsgBoxStyle.Information, "Salva Dados")

             End Try

        Case vbNo

             '//--- Nada

      End Select

  oDS_Delta = Nothing

  oCustomer = Nothing

End Sub

 

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

   LoadData()

End Sub

 

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

   SaveData()

End Sub

 

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click

   Me.Dispose()

End Sub

 

End Class

- Quando o usuário clicar no botão - Carregar Dados - o método LoadData() será invocado . Nele estamos declarando e criando uma instância da classe serviços de negócios :

Dim oCustomer As BusinessServices.Customer = New BusinessServices.Customer()

- A seguir declaramos a vinculação de dados e iniciamos com o status clear (limpo). Então usamos o método GetData do objeto de serviços de negócios - oCustomer - para retornar o DataSet preenchido com os dados da tabela - Customer - para o formulário através do DataGrid. Feito isto o DataSet será local e estará vinculado ao DataGrid.

   grdData.DataBindings.Clear()

   m_oDS = oCustomer.GetData()

   grdData.DataSource = m_oDS.Tables("Customer")

   oCustomer = Nothing

- Com os dados preenchendo o DataGrid e sendo exibidos no formulário o usuário poderá interagir com os dados via interface : alterando , excluindo e incluindo novos registros. Para salvar as alterações o usuário irá clicar no botão - Salvar Dados - que irá invocar o método SaveData().

- O método SaveData() cria uma instância dos objetos de serviços - BusinessServices.Customer() - e verifica se houve qualquer alteração nos dados usando o método HasChanges(). Como o DataSet esta vinculado ao DataGrid qualquer alteração feita será refletida no DataSet. Havendo alterações eu utilizo a variável   oDS_Alterados para armazenar os registros alterados :        oDS_Alterados = m_oDS.GetChanges()

- O método GetChanges do DataSet copia as linhas alteradas do DataSet e as coloca em um novo DataSet: oDS_Alterados.

- Finalmente o usuário deverá confirmar se deseja salvar as alterações

A camada de serviços

A camada de serviços é definida em uma classe chamada : BusinessServices.  O código da classe esta descrito a seguir :

'//--------------------------------------------------------------------------------------------------------

'// Declara todos os NameSpaces que serão referenciados

'//--------------------------------------------------------------------------------------------------------

Imports System

Imports System.Data

Imports System.Data.OleDb

'//--------------------------------------------------------------------------------------------------------------

'// NameSpace BusinessServices

'//--------------------------------------------------------------------------------------------------------------

Namespace BusinessServices

Public Class Customer

'//----------------------------------------------------------------------------------------------------

'// Declara as variáveis de Classe

'//----------------------------------------------------------------------------------------------------

Private m_sProvider As String = "PROVIDER=Microsoft.Jet.OLEDB.4.0;"

Private m_sServer As String = "DATA SOURCE=C:/teste/" '

Private m_sDatabase As String = "Northwind1.mdb"

Private m_oDS As DataSet

Private m_oCn As OleDbConnection

Private m_oDA As OleDbDataAdapter

Private m_sClassName As String = "Customer" '//--- o nome da classe

'//----------------------------------------------------------------------------------------------------

'// Constructor (sem argumentos)

'//----------------------------------------------------------------------------------------------------

Sub New()

Dim sSQL As String = ""

Dim oSelCmd As OleDbCommand

Dim oInsCmd As OleDbCommand

Dim oUpdCmd As OleDbCommand

Dim oDelCmd As OleDbCommand

'---------------------------------------------------------

'--- define a conexao

'---------------------------------------------------------

InitializeConnection()

'---------------------------------------------------------

'--- define o SELECT Command

'---------------------------------------------------------

sSQL = "SELECT CustomerID, CompanyName, ContactName, City, Region FROM Customers ORDER BY CompanyName "

 

oSelCmd = Nothing

oSelCmd = New OleDbCommand(sSQL, m_oCn)

oSelCmd.CommandType = CommandType.Text

'---------------------------------------------------------

'--- define o UPDATE Command

'---------------------------------------------------------

sSQL = "UPDATE Customers " & _

" SET CompanyName = @CompanyName , ContactName = @ContactName, City = @City, Region = @Region WHERE CustomerID = @CustomerID "

 

oUpdCmd = Nothing

oUpdCmd = New OleDbCommand(sSQL, m_oCn)

 

With oUpdCmd

   .CommandType = CommandType.Text

   With .Parameters

      .Add(New OleDbParameter("@CompanyName", OleDbType.VarChar, 40, "CompanyName"))

      .Add(New OleDbParameter("@ContactName", OleDbType.VarChar, 30, "ContactName"))

      .Add(New OleDbParameter("@City", OleDbType.VarChar, 15, "City"))

      .Add(New OleDbParameter("@Region", OleDbType.VarChar, 15, "Region"))

      .Add(New OleDbParameter("@CustomerID", OleDbType.LongVarChar, 5, "CustomerID"))

   End With

End With

'---------------------------------------------------------

'--- define o INSERT Command

'---------------------------------------------------------

sSQL = "INSERT INTO Customers " & _

" (CompanyName, ContactName, City, Region, CustomerID) VALUES (@CompanyName, @ContactName, @City, @Region, @CustomerID)"

oInsCmd = Nothing

oInsCmd = New OleDbCommand(sSQL, m_oCn)

 

With oInsCmd

   .CommandType = CommandType.Text

    With .Parameters

     .Add(New OleDbParameter("@CompanyName", OleDbType.VarChar, 40, "CompanyName"))

     .Add(New OleDbParameter("@ContactName", OleDbType.VarChar, 30, "ContactName"))

     .Add(New OleDbParameter("@City", OleDbType.VarChar, 15, "City"))

     .Add(New OleDbParameter("@Region", OleDbType.VarChar, 15, "Region"))

     .Add(New OleDbParameter("@CustomerID", OleDbType.LongVarChar, 5, "CustomerID"))

    End With

End With

'---------------------------------------------------------

'--- define o DELETE Command

'---------------------------------------------------------

sSQL = "DELETE Customers WHERE CustomerID = @CustomerID "

oDelCmd = Nothing

oDelCmd = New OleDbCommand(sSQL, m_oCn)

With oDelCmd

    .CommandType = CommandType.Text

    With .Parameters

       .Add(New OleDbParameter("@CustomerID", OleDbType.LongVarChar, 5, "CustomerID"))

     End With

End With

'---------------------------------------------------------

'--- cria e define os comandos para o DataSet

'---------------------------------------------------------

m_oDA = New OleDbDataAdapter()

With m_oDA

  .SelectCommand = oSelCmd

  .UpdateCommand = oUpdCmd

  .DeleteCommand = oDelCmd

  .InsertCommand = oInsCmd

End With

'//--- Destroi o objeto connection

m_oCn = Nothing

End Sub

'//------------------------------------------------------------------------------------------------------------

Public Function SaveData(ByVal oDS As DataSet) As String

 

Dim sMsg As String

Dim lRecsAffected As Long

Dim oTran As OleDb.OleDbTransaction

'---------------------------------------------------------

'//--- Salva os dados

'---------------------------------------------------------

Try

'--- define a conexao

InitializeConnection()

   m_oCn.Open()

   '--- inicia a transacao

   oTran = m_oCn.BeginTransaction()

   '--- atualiza o banco de dados

   lRecsAffected = m_oDA.Update(oDS, m_sClassName)

    '--- consolida (commit) as mudanças

    oTran.Commit()

   '--- define as mensagens para o usuario

   sMsg = lRecsAffected & " registros foram atualizados"

   Catch e As Exception

        '--- Ocorreu um erro Desfaz a transacao

       oTran.Rollback()

       sMsg = "Os registros não foram atualizados." & vbCrLf & e.Message.ToString()

   Finally

'--- Fecha a conexao

oTran = Nothing

m_oCn.Close()

m_oCn = Nothing

SaveData = sMsg

End Try

End Function

'//------------------------------------------------------------------------------------------------------------

// retorna os dados da tabela clientes

'//------------------------------------------------------------------------------------------------------------

Public Function GetData() As DataSet

  '--- Cria um novo datase

  m_oDS = New DataSet()

   '--- preenche o dataset com os dados dos clientes

  m_oDA.Fill(m_oDS, m_sClassName)

  '//--- Retorna o dataset

  Return m_oDS

End Function

 

'//------------------------------------------------------------------------------------------------------------

'// Inicializa a conexão

'//------------------------------------------------------------------------------------------------------------

Private Sub InitializeConnection()

  '--- define a conexao

  m_oCn = New OleDbConnection(m_sProvider & m_sServer & m_sDatabase)

End Sub

 

End Class

End Namespace

 

vai continuar ...


José Carlos Macoratti