Como criar uma Aplicação Multiusuário com ADO ?
Acho que esta pergunta já deve ter povoado
seus pensamentos. E a resposta ?
Ora ! a resposta ? quem precisa dela ? |
Na verdade para muitos desenvolvedores que antes programavam em Clipper ( o velho e bom Clipper ...) ou outra linguagem padrão Xbase e estão migrando para uma ferramenta Visual ( VB é claro..) a pergunta é frequente . O problema é que a resposta , pelo menos uma reposta que não seja apenas teórica , é rara.
Vamos tentar ( eu disse tentar... ) mostrar de forma clara e detalhada como usar o VB e ADO para criar uma aplicação que seja multiusuário (Não confunda com ambiente Cliente/Servidor). Espero que após ler este artigo até o fim ( ele vai ser disponibilizado por etapas...) você seja capaz de ter essa pergunta respondida.
O que é uma aplicação desenvolvida para um ambiente Multiusuário ?
Uma aplicação desenvolvida para um ambiente multiusuário deverá permitir o acesso de mais de um usuário ao sistema e controlar todas as implicações advindas da concorrência que isto pode acarretar. Ou seja se eu estiver acessando o cadastro de clientes para incluir um novo cliente a aplicação tem que levar em conta que outro usuário poderá estar fazendo a mesma coisa no mesmo momento. Isto sem falar em tentar editar um registro que acabou de ser excluído por outro usuário , e por ai vai...
É claro que o comportamento do meu sistema vai depender de diversos fatores , entre os quais destacamos a base de dados com a qual vamos trabalhar. Nosso estudo de caso irá usar uma base Access , um arquivo mdb , pois creio ser a mais usada neste ambiente.
A teoria sobre o comportamento do Access quando trabalhamos num ambiente multiusuário é vastíssima e não temos a intenção de teorizar mas de praticizar (uau! que neologismo...). Por isto não espere profundas análises neste artigo , vamos focar o prático do dia a dia. (Nada de camarão , ficamos com o arroz com feijão.)
O banco de dados e as tabelas
Neste artigo eu estou supondo que você saiba o que é um banco de dados e o que é uma tabela , e também que você saiba distinguir uma tabela de um banco de dados. Embora o Access tenha uma interface muito amigável e torne bem mais fácil a tarefa de criar banco de dados e tabelas com seus relacionamentos e tudo mais , não pense que é só sentar na frente do micro e ir criando seu banco de dados e tabelas que sua aplicação já vai pode estar funcionando. Creio que o fundamental de uma aplicação comercial com banco de dados é justamente a análise do sistema com um banco de dados enxuto e tabelas normalizadas.
Dependendo do tamanho da aplicação , a utilização de uma ferramenta Case irá tornar o trabalho ainda muito mais produtivo e fácil. Se você quer criar um cadastro de clientes que envolve uma tabela apenas tudo bem , mas quando você começa a usar duas , três , .., dez tabelas , a coisa vai complicando.
Então , para estes casos , é preciso ir com calma no desenho e criação da base de dados , pois ela vai ser o fundamento da sua aplicação. Já pensou em depois de construir uma casa , estando na fase de acabamento , ter que mexer no alicerce ? Pois é mais o menos isto que acontece com aplicações com tabelas mal desenhadas e não normalizadas.
O inicio - Desenhando a base de dados
Definindo o nosso esquema de trabalho:
|
|
Tabela Produtos |
Tabela Categorias |
|
Tabela Vendas |
|
Relacionamento entre as tabelas |
Descrevendo as tabelas do sistema:
A tabela Produtos contém de forma simplificada informações sobre o produto que iremos tratar : que tal CD´s. Sim você possui uma loja que vende CD´s de música e vai criar uma aplicação multiusuário para cadastrar o seu produto. O formulário da aplicação deverá permitir ao usuário informar o Nome do Produto , a Categoria e se o produto esta ou não em promoção.
Esqueci o Código do Produto ? Não !!! Este campo (ProdutoID) é definido como sendo do tipo Autonumeração , então o próprio banco de dados irá cuidar de incrementar o Código do produto não permitindo assim codigos duplicados.
A tabela Categoria deverá indicar a que categoria o produto pertence. No nosso caso vamos pensar nesta categoria como o tipo de música do produto ( CD ) : Vamos cadastrar somente 3 Tipos : Rock , MPB e Jazz. O campo CategoriaID identifica o código da categoria e é do tipo Autonumeração e chave primária não permitindo duplicações neste item.
A tabela Vendas contém o código do produto , a quantidade e o percentual das vendas. O código do produto ( ProdutoID) esta indexado e permite a duplicação.
Os relacionamentos
Temos um relacionamento entre o campo CategoriaID (tabela Categorias) e o campo ProdutoCategoria (tabela Produtos) . O relacionamento do tipo um-para-muitos: (ver abaixo)
O Outro relacionamento é entre o campo ProdutoID (tabela Produtos) e o campo ProdutoID (Vendas). O relacionamento é do tipo um-para-muitos. (ver abaixo)
Por que criamos este relacionamentos ??? Bem , ao impor a integridade referencial deixamos a cargo do Banco de dados verificar , no caso dos campos relacionados , a validade da inclusão dos registros. No nosso caso somente poderemos incluir na tabela Produtos uma categoria que exista previamente cadastrada na tabela Categorias. Com isso evitamos registros orfãos. Se você tentar informar uma categoria que não esteja cadastrada ocorrerá um erro que cabe a você tratar e direcionar através de uma mensagem amigável ao usuário. ( Algo do tipo : Se toca cara... )
Criando a interface com o usuário
Vamos criar agora a interface com o usuário. Nosso projeto terá dois formulários básicos: o formulário principal ( frmprincipal) e o formulário de dados ( frmadados).
1-) O formulário principal - Abaixo temos o jeitão do formulário principal:
O formulário principal contém um controle PictureBox que servirá como container , i.e, conterá os demais controles que comporão o que pode ser chamado de menu da aplicação. O controle Label exibe uma explicação ao usuário de que ele deve clicar duas vezes para gerar duas instâncias ( no mínimo ) do formulário de dados. Os ícones - Gravar Dados e Sair - são inseridos em controles Image e permitem o acesso ao formulário de dados e encerrar o sistema.
|
2-) O formulário de dados - O formulário de dados permitirá a manutenção de dados de nossa tabela de produtos.
Nele temos as opções : Editar , Incluir, Excluir , Refresca e Sair. Veja figura abaixo:
O formulário de dados contém labels de para identificar os campos que serão exibidos em controles TextBox ( Código , Nome Produto, Categoria ) e o controle checkbox estará informando se o produto esta em promoção. Os botões de comando - Editar, Incluir, Excluir, Refresca e sair - permitem a operação com os registros da tabela. A navegação pelo banco de dados é feita pelos botões de comando com os sinais: |< , < e > >| ) |
Como funciona ?
Nosso objetivo é mostrar como funciona uma aplicação multiusuário. Para fazer os testes, o ideal seria ter realmente uma aplicação instalada em uma rede , mas para podermos permitir os testes em uma máquina local vamos criar duas ou mais instâncias de nossa aplicação para simular o ambiente multiusuário.
Vejamos o código do formulário principal:
Private Sub Image1_Click() 'Abre uma instância dodo formulario de dados Dim frmNew As frmSingleRec Static intFormContador As Integer On Error GoTo trata_erros intFormContador = intFormContador + 1 Set frmNew = New frmDados Load frmNew frmNew.Caption = "Formulários de Dados Multiusuário, Instancia => #" & intFormContador frmNew.Show Exit Sub trata_erros: MsgBox Err.Description End Sub |
Quando clicamos pela primeira vez em Gravar Dados temos uma instância do formulário frmDados criada e exibida ao usuário. A partir dai toda vez que clicarmos esta opção iremos estar criando uma nova instância do formulário simulando com isto a concorrência de um ambiente multiusuário. ( Uma outra forma de você fazer isto sem criar várias instâncias de um mesmo formulário seria abrir o banco de dados com Access ,e , deixando-o aberto iniciar o aplicativo gerando uma instância do formulário)
Private Sub Form_Unload(Cancel As
Integer) Dim i As Integer On Error GoTo trata_erros While Forms.Count > 1 i = 0 While Forms(i).Caption = Me.Caption i = i + 1 Wend Unload Forms(i) Wend Unload Me End Exit Sub trata_erros: MsgBox Err.Description End Sub |
Vamos executar o aplicativo e criar duas instâncias do formulário para entrada de dados. A seguir vamos simular as situações de concorrência que podem acontecer no dia a dia de um sistema multiusuário:
Situação 1 - Um usuário A esta editando o registro 1 . Neste exato momento em outra estação o usuário B resolveu excluir o registro 1. O que deve acontecer ?
- O sistema deve estar pronto para avisar ao usuário B que o registro 1 esta bloqueado para edição.
O código responsável por isto é dado a seguir:
Private Sub cmdexcluir_Click() If MsgBox("O registro será excluido definitivamente. Continua ?", vbYesNoCancel + vbExclamation, "Confirma Exclusão") <> vbYes Then Exit Sub On Error Resume Next mrsPrimary.Delete Select Case Err.Number Case 0: 'exclusao foi um sucesso If cmdMoveNext.Enabled Then cmdMoveNext_Click Else If cmdMovePrevious.Enabled Then cmdMovePrevious_Click End If Case -2147217864 MsgBox "Esta linha já foi excluida por outro usuário!", vbInformation mrsPrimary.CancelUpdate Case -2147467259 MsgBox "As alterações feitas não podem ser salvas no momento. O registro encontra-se bloqueado pelo por outro usuario." & vbCr & "Voce pode cancelar as alteracoes ou tentar salvar mais tarde...", vbExclamation, "Erro de gravacao" Exit Sub Case Else MsgBox "O registro nao pode ser excluido." + vbCrLf + Err.Description mrsPrimary.CancelUpdate End Select Exit Sub Trata_Erro: |
Aqui interceptamos os possíveis erros que ocorreriam neste caso e exibimos a mensagem ao usuário. No caso o erro -2147467259 ocorre .
- Situação 2 - O usuário A e o usuário B estão editando o mesmo registro ( no caso o registro 1) . O usuário A terminou de fazer suas alterações e salvou-as. Em seguida o usuário B termina de fazer suas alterações e tenta salvá-las. O que ocorre ?
- O sistema não deve permitir que o usuário B salve as alterações , deve avisá-lo que o registro acabou de ser alterado por outro usuário.
- O código responsável por essa façanha é o seguinte:
Private Sub cmdsalvar_Click() 'Salva Dim vFieldArray(), x As Integer, intUpdateError As Integer, strErrorMessage As String, oError As Error Dim blnAdd As Boolean On Error Resume Next 'limpa o objeto error blnAdd = mrsPrimary.EditMode = adEditAdd mrsPrimary.ActiveConnection.Errors.Clear Screen.MousePointer = vbHourglass mrsPrimary.Update Screen.MousePointer = vbNormal intUpdateError = Err.Number 'armazena os erros(O objeto error sera resetado pelo proxima linha) On Error GoTo TrataErros Select Case intUpdateError Case 0: If mrsPrimary.ActiveConnection.Errors.Count = 0 Then 'nao ocorreu nenhum erro Modo_Edicao (Navegacao) Atualiza_Botoes_Navegacao_Posicao If blnAdd Then mrsPrimary.Resync adAffectCurrent 'exibe os valores padrao mrsPrimary.Move 0 'forca uma atualiacao dos controles para exibir os dados End If Else For Each oError In mrsPrimary.ActiveConnection.Errors If oError.Number = -2147217864 Then strErrorMessage = "Este registro já foi recentemente alterado por outro usuário ! " MsgBox strErrorMessage Exit Sub Else strErrorMessage = strErrorMessage & oError.Description & vbCr End If Next If mrsPrimary.ActiveConnection.Errors.Count = 1 Then strErrorMessage = "Os seguintes erros" & _
IIf(mrsPrimary.ActiveConnection.Errors.Count > 1, "", " foram ") & "
definidos pelo provedor : " & vbCr &
strErrorMessage MsgBox strErrorMessage ' exibe todos os erros
End If
Then If cmdatualizar.Visible Then
cmdatualizar.SetFocus |
O código do botão para editar os registros - Editar - é mostrado a seguir , nele estamos prevendo o caso do registro que esta sendo editado ter sido excluído por outro usuário.
Private Sub cmdeditar_Click() On Error GoTo Trata_Erro mrsPrimary.Resync adAffectCurrent 'pega o ultimo dado para editar mrsPrimary.Move 0 'atualiza os controles Modo_Edicao (Editando) Atualiza_Botoes_Navegacao_Posicao (EditMode) Exit Sub Trata_Erro: Select Case Err.Number Case -2147217885 'a linha foi excluida MsgBox "Esta linha foi excluida por outro usuario...", vbInformation Case Else Exibe_Erros (Err.Description) End Select End Sub |
Voce já percebeu que para tratar os conflitos no ambiente multiusuário basta prever e tratar os erros que serão gerados pela aplicação. Basta planejar com cuidado e testar com paciência , não há segredo nenhum no código envolvido. Você somente vai ter que saber os possíveis erros gerados em tempo de execução pelos conflitos , tratá-los através de uma interface amigável e de forma transparente ao usuário.
Creio que você já teve uma noção de como lidar com a programação em ambiente multiusuário. É obvio que não esgotei o assunto , na verdade nem arranhei , mas o que foi mostrado até aqui o capacitará a adaptar o código as suas necessidades além de corrigir alguns bugs presentes no meu exemplo. :-)
Para pegar uma cópia do projeto clique no link -> Adomulti.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:
Super DVD Vídeo Aulas - Vídeo Aula sobre VB .NET, ASP .NET e C#
Super DVD C# - Recursos de aprendizagens e vídeo aulas para C#
Curso Fundamentos da Programação Orientada a Objetos com VB .NET