VB.NET - Criando um aplicação com DataSets não tipados e Provedores de dados
Neste artigo você vai aprender a criar programas que acessam banco de dados relacionais usando datasets não tipados e os provedores de dados , ou seja, você vai aprender a criar programas que acessam base de dados relacionais sem usar os assistentes do VS.NET , vai fazer tudo no código. Ao terminar de ler este artigo , se você seguir as orientações e por realmente a mão na massa vai ter aprendido a :
criar e trabalhar com objetos provedores de dados via código
criar e trabalhar com datasets não tipados via código
Pronto para começar ?
Nota: Este é um artigo para iniciantes e se você precisa criar uma aplicação mais complexa é melhor usar uma ferramenta ORM como o Entity Framework ou NHibernate. Veja a seção para esses recursos no site.
Introdução
Um DataSet não tipado é um dataset que não é baseado em um esquema de dados , ou seja , ele é criado via código através da classe DataSet da ADO.NET.
Você vai usar os métodos e propriedades da classe DataSet para criar e trabalhar com os datasets não tipados. A figura a seguir exibe o modelo resumido de conexão ADO.NET
Abaixo temos um figura onde visualiza-se os provedores de dados e o Dataset na arquitetura ADO.NET:
Um DataSet é um objeto de dados
desconectado e a informação no interior de um dataset esta armazenada em várias
coleções : DataRows, DataTables, DataColumns, etc. Conforme figura abaixo:
As propriedades usadas com as coleções de
dados são :
|
Desvantagens na utilização de DataSets não tipados (Untyped DataSets)
Como a referência aos datasets não tipados não é verificada em tempo de compilação muitos erros podem ocorrer em tempo de execução.
Você não pode usar o recurso de IntelliSense para ajudá-lo durante a tarefa de codificação.
Leva mais tempo para escrever o código da sua aplicação
Os datasets não tipados seriam menos eficientes que os datasets tipados ; estes deveriam ser usados sempre que possível.
Com todas estas desvantagens por que eu estou abordando os dataset não tipados ?
Porque em alguns ambientes de desenvolvimento o esquema da fonte de dados não está disponível em tempo de projeto e por que tem muita desenvolvedor que gosta de escrever o seu próprio código.
Criando um projeto de acesso a dados usando datasets não tipados
1- Inicie um novo projeto no VS.NET do tipo Windows Application usando a linguagem VB.NET, dando a ele o nome de : dsClientes.
2- Altere o formulário padrão -
form1.vb ; altere o seu nome para : frmClientes , construindo no
formulário o layout como o indicado na figura abaixo.
- Clique com o botão direito sobre o nome
do projeto e defina Startup Object como
frmClientes - Os nomes indicados na figura ao lado são os nomes usados nos controles. - Defina a propriedade ReadOnly como True para todos os controles TextBox - Defina a propriedade TabStop para o controle cboClientes e para todos os botões de controle como False - Defina a propriedade TabOrder para os controles TextBox e CboEstado de forma a estarem na ordem correta. NÃO inclua nenhum objeto Connection, DataAdpater ou DataSet no formulário. |
A seguir temos os requisitos de como o sistema deve se comportar:
Criando os objetos Provedores de Dados - Data Provider Objects
Como nossa aplicação é uma aplicação que vai acessar uma fonte de dados devemos dar prioridade para o tratamento a conexão com o banco de dados.
Na plataforma .NET podemos usar os provedores de dados para realizar esta tarefa. Dependendo da base de dados que iremos acessar podemos otimizar esta tarefa escolhendo o provedor adequado. Assim para acessar o SQL Server temos o provedor SqlClient , para acessar uma base de dados Access temos o provedor OleDB , para acessar uma base Oracle temos o SqlOra, etc.
Para detalhes neste assunto leia os artigos :
Criando um objeto SQLConnection
Para efetuar uma conexão com uma fonte de dados SQL Server usamos o provedor : SQL Server .NET Data Provider (System.Data.SqlClient)
A primeira coisa a fazer é importar em seu projeto o namespace System.Data.SqlClient
Podemos definir uma string de conexão da seguinte forma :
Imports
System.Data.SqlClient Dim strConn As String = "Server=JCM\Mac;database=Teste; integrated security = False; workstation ID=320; persist security info= False;" _ & "packet size=4096; user id=mac; password=123456"
Dim conexao = New SqlConnection() ou você pode fazer armazenar o valor da string de conexão como parte da declaração : Dim conexao = New SqlConnection(strConn) |
Para abrir e fechar uma conexão usamos os métodos : conexao.Open() e conexão.Close()
Vamos relacionar o significado dos parâmetros usados na string de conexão para o SQL Server :
É claro que nem todos os parâmetros são obrigatórios. Quais são ?
Vai depender da conexão e do tipo de segurança que você deseja usar. Podemos
usar o seguinte código para uma conexão :
Imports System.Data.SqlClient ... Dim oSQLConn As SqlConnection = New SqlConnection() oSQLConn.ConnectionString = "Data Source=(local);Initial Catalog=MeuBancoDados;Integrated Security=SSPI" oSQLConn.Open() |
Criando um objeto OleDbConnection
Para criar uma conexão com um banco de dados Access usamos o provedor : OLE DB .NET Data Provider (System.Data.OleDb)
Geralmente temos que informar o valor para DataSource incluindo o caminho do banco de dados:
Abaixo temos um exemplo de conexão usando o provedor OleDb:
Dim oOleDbConnection As OleDb.OleDbConnection Dim sConnString As String = "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=C:\Caminho\NomeBancodeDados.mdb;" & _ "User ID=Admin;" & _ "Password=" oOleDbConnection = New OleDb.OleDbConnection(sConnString) oOleDbConnection.Open() |
Onde podemos armazenar a string de conexão ?
A melhor maneira de fazer isto é armazenar a informação sobre a conexão em arquivos externos como um arquivo texto ou arquivo XML. Você pode também armazenar a string de conexão no arquivo de configuração da aplicação. Para saber mais sobre o assunto leia o artigo : NET - Tratando arquivos de configuração.
Criando o código para a aplicação Manutenção de Clientes
A aplicação irá trabalhar com duas tabelas : a tabela Clientes e a tabela Estados. A estrutura de cada uma é exibida abaixo:
|
|
Tabela Clientes |
tabela Estados |
Nossa aplicação vai usar uma conexão com o SQL Server por isto devemos especificar a cláusula Imports para usar o namespace System.DataSqlClient. Vamos definir também Option Strict como On
Option
Strict One a seguir definir as variáveis usadas no sistema:
Dim blnLoad As Boolean Dim blnNovaLinha As Boolean Dim bmClientes As BindingManagerBaseDim dsClientes As New DataSet Dim daClientes As New SqlDataAdapter Dim daEstados As New SqlDataAdapter Dim con As New SqlConnection |
Option Strict On -> Não permite conversão implícita de variáveis de tipos diferentes. O default é Off.
As duas variáveis booleanas serão usadas para controlar a execução do código durante a carga inicial do programa e enquanto um nova linha(registro) for incluída no DataSet..
As demais variáveis objetos serão usadas no tratamento de conexão e dados .
O código do evento Load do formulário:
No evento Load do formulário iremos colocar o código
Private
Sub frmClientes_Load(ByVal
sender As System.Object,
ByVal e As System.EventArgs)
Handles MyBase.Load
blnLoad = True iniciaObjetos() preencheTabelas() ligaControles()
'Chama a rotina passando True - habilita os botões : Salvar , Incluir , Cancelar
defineBotes(True) 'Define a propriedade BindingContext property do BindingManagerBase bmClientes = Me.BindingContext(dsClientes, "Clientes")
MessageBox.Show("Ocorreu o seguinte erro : " & ControlChars.NewLine & eData.Message, "ADO.NET Error", MessageBoxButtons.OK, MessageBoxIcon.Error) Catch eSQL As SqlException Dim intError As Integer Dim SqlErrors As SqlErrorCollection = eSQL.Errors
Dim strErrorResumo As
String = "Ocorreu o seguinte erro : " 'Armazena o erro em strErrorResumo For intError = 0 To SqlErrors.Count() - 1 strErrorResumo &= SqlErrors(intError).Number & " – " & SqlErrors(intError).Message & ControlChars.NewLine Next intError MessageBox.Show(strErrorResumo, "Sql Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error) Catch eSystem As Exception MessageBox.Show("Ocorreu o seguinte erro : " & eSystem.Message, "System Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End
Try |
A rotina iniciaObjetos tem o seguinte código :
Private
Sub IniciaObjetos()
Dim strConnection As String = "Data Source=(local);Initial Catalog=Macoratti;Integrated Security=SSPI"
con.ConnectionString = strConnection 'Declare o objeto Comando cmdClientes Dim cmdClientes As New SqlCommand cmdClientes.Connection = con
Dim strClienteselect As String = "Select Codigo, Nome, Endereco, Cidade, Estado, Cep FROM Clientes ORDER BY Nome" cmdClientes.CommandText = strClienteselect
daClientes.SelectCommand = cmdClientes
Dim cbClientes As New SqlCommandBuilder cbClientes.DataAdapter = daClientes
Dim cmdEstados As New SqlCommand cmdEstados.Connection = con
cmdEstados.CommandText = strEstadoselect
daEstados.SelectCommand = cmdEstados End Sub
|
Comentando o código acima:
Se estivéssemos usando datasets tipados não teríamos que nos preocupar com objeto SqlCommand que no caso armazena o atual comando SQL Select que quando executado irá retornar um dataset. Ele é gerado automaticamente como parte do objeto DataAdapter e é passado para o objeto Connection quando necessário. No nosso caso temos que criar o objeto SqlCommand.
Existem duas propriedades que precisam ser definidas para um novo objeto SqlCommand : Connection e Commandtext
Outras propriedades:
O procedimento para preencher as tabelas : abre a conexão e preenche os objetos DataAdpater - temos dois : daClientes e dasEstados. Perceba que estou colocando no dataset dsClientes as duas tabelas.
Private Sub preencheTabelas() Try 'Abre a conexão, preenche as tabelas e fecha a conexão. con.Open() daClientes.Fill(dsClientes, "Clientes") daEstados.Fill(dsClientes, "Estados") con.Close()
Dim SqlErrors As SqlErrorCollection = eSQL.Errors Dim strErrorResumo As String = "Error(s): "
'Armazena o erro em strErrorResumo For intError = 0 To SqlErrors.Count() - 1 strErrorResumo &= SqlErrors(intError).Number & " – " & SqlErrors(intError).Message & ControlChars.NewLine Next intError MessageBox.Show(strErrorResumo, "Sql Server Error", MessageBoxButtons.OK, MessageBoxIcon.Error) Catch eSystem As Exception MessageBox.Show("Ocorreu o seguinte erro : " & eSystem.Message, "System Error", MessageBoxButtons.OK,MessageBoxIcon.Error)
End
Try End Sub
|
O procedimento ligaControles vincula os controles ComboBox e TextBox com as colunas das tabelas apropriadas : Clientes e Estados.
As combobox irão exibir o nome completo de cada estado , mas quando uma nova linha for incluída na tabela Clientes, o código do estado é que será armazenado na tabela Clientes através do uso da propriedade ValueMember do combobox.
Private Sub ligaControles() 'vinculcao da combobx clientes cboClientes.DataSource = dsClientes.Tables("Clientes") cboClientes.DisplayMember = "Nome"
cboEstados.DataSource = dsClientes.Tables("Estados") cboEstados.DisplayMember = "Nome" cboEstados.ValueMember = "Estado" cboEstados.DataBindings.Add("SelectedValue", dsClientes, "Clientes.Estado")
txtNome.DataBindings.Add("Text", dsClientes, "Clientes.Nome") txtEndereco.DataBindings.Add("Text", dsClientes, "Clientes.Endereco") txtCidade.DataBindings.Add("Text", dsClientes, "Clientes.Cidade") txtCep.DataBindings.Add("Text", dsClientes, "Clientes.Cep")
|
Agora o código da rotina defineBotoes():
Private
Sub defineBotes(ByVal
blnValor As Boolean)
btnIncluir.Enabled = blnValor btnAtualizar.Enabled = blnValor btnExcluir.Enabled = blnValor btnCancelar.Enabled = Not blnValor btnSalvar.Enabled = Not blnValor btnSair.Enabled = blnValor
|
Neste momento você já pode executar o projeto que o formulário irá exibir os dados da tabela e as combobox serão preenchidas também.
Esta faltando incluir as funcionalidades referente as ações dos botões e das combobox. Vou fazer isto em breve...
Implementando o código para as ComboBox
Quando o usuário mudar a seleção do nome do cliente na combobox o evento SelectedIndexChanged será disparado. A propriedade SeletecdIndex do controle Combobox é armazenado pela propriedade Position do objeto BindingManagerBase a fim de manter os dados exibidos nas caixas de texto sincronizados com o nome da combobox.
Os botões para manter os dados são habilitados e o foco é desviado para a caixa de texto Nome , que exibe o nome do cliente.
O código que faz todo este tratamento no evento da combobox é o seguinte :
Private Sub
cboClientes_SelectedIndexChanged(ByVal sender
As System.Object, ByVal
e As System.EventArgs)
Handles cboClientes.SelectedIndexChanged
bmClientes.Position = cboClientes.SelectedIndex defineBotoes(True) txtNome.Focus() End If End Sub |
O código do botão de comando Incluir
Quando o botão Incluir é pressionado o evento Click será disparado. No código definimos a variável blnNovaLinha com valor True e chamamos a rotina para 'travar' as caixas de texto com o valor False permitindo assim que possamos incluir dados nas caixas de texto.
O método AddNew de mbClientes inclui uma linha vazia e é chamada a rotina para defineBotes como parâmetro igual a False , com isto habilitamos os botões Salvar e Cancelar enquanto desabitamos todos os outros durante a operação de inclusão de dados. Definimos o valor da combobox para os estados como cboEstados.SelectedIndex = -1 e colocamos o foco na caixa de texto nome
Private Sub btnIncluir_Click(ByVal
sender As System.Object,
ByVal e As System.EventArgs)
Handles btnIncluir.Click
bmClientes.AddNew() defineBotoes(False) cboEstados.SelectedIndex = -1
|
Vamos exibir então o código da rotina TravaCaixaTextos() que foi usada acima:
Private Sub TravaCaixaTextos(ByVal
blnvalor As Boolean)
txtEndereco.ReadOnly = blnvalor txtCidade.ReadOnly = blnvalor txtCep.ReadOnly = blnvalor
|
Agora o código do botão - Atualizar :
No evento Click deste botão chamamos a rotina TravaCaixaTextos como parâmetro False para permitir que as alterações nas caixas de texto possam ser feitas. A rotina defineBotes() também é chamada com o parâmetro False para habilitar os botões Salvar e Cancelar
Private
Sub btnAtualizar_Click(ByVal
sender As System.Object,
ByVal e As System.EventArgs)
Handles btnAtualizar.Click travaCaixaTextos(False) defineBotoes(False) End Sub |
Excluindo registros
Na exclusão de um registro usamos uma variável para armazenar o nome do cliente ,e , assim poder restaurar os valores se ocorrer alguma exceção.
Ao clicar no botão Excluir uma caixa de mensagem é exibida o usuário para que ele confirme a exclusão. Na confirmação temos:
Se ocorrer alguma exceção estou fazendo o tratamento para tratar erro devido a concorrência , erro de integridade referencial , erro ADO.NET e erro genérico. Em todos os casos a rotina carregaDataSetClientes() é invocada para restaurar os valores no DataSet.
Abaixo o código completo:
Private Sub btnExcluir_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExcluir.Click Dim strClienteNome As String = txtNome.Text Dim dgrResultado As DialogResult = MessageBox.Show("Deseja Excluir " & cboClientes.Text & " ? ", "Confirma a exclusão do cliente", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) Try 'se pressionou o botão Sim , então remove o cliente e atualiza o dataset If dgrResultado = DialogResult.Yes Then Me.Cursor = Windows.Forms.Cursors.WaitCursor bmClientes.RemoveAt(bmClientes.Position) daClientes.Update(dsClientes, "Clientes") End If Catch eConstraint As ConstraintException 'ocorreu um erro nos valores dos dados; o dataset é recarregado MessageBox.Show("Ocorreu um erro : " & ControlChars.NewLine & eConstraint.Message, "Erro no valos dos dados", MessageBoxButtons.OK, MessageBoxIcon.Error) recarregaDataSetClientes(strClienteNome) Catch eConcorrencia As DBConcurrencyException 'ocorreu um erro de concorrência na exclusão dos registros MessageBox.Show("Ocorreu um erro : " & ControlChars.NewLine & eConcorrencia.Message, "Erro na exclusão do registro.", MessageBoxButtons.OK, MessageBoxIcon.Error) recarregaDataSetClientes(strClienteNome) Catch eDados As DataException 'ocorreu um erro ADO.NET MessageBox.Show("Ocorreu um erro : " & ControlChars.NewLine & eDados.Message, "erro ADO.NET.", MessageBoxButtons.OK, MessageBoxIcon.Error) recarregaDataSetClientes(strClienteNome) Catch eSistema As Exception 'ocorreu um erro genérico MessageBox.Show("Ocorreu um erro : " & ControlChars.NewLine & eSistema.Message, "erro ADO.NET.", MessageBoxButtons.OK, MessageBoxIcon.Error) recarregaDataSetClientes(strClienteNome) Finally 'define o cursor do mouse e o foco Me.Cursor = Windows.Forms.Cursors.Arrow cboClientes.Focus() End Try |
A rotina para recarregar o DataSet
A rotina recarregaDataSetClientes limpa a tabela Clientes do dataset e então chama as rotinas iniciaObjetos e preencheTabelas que reconstroem o dataset.(Esta rotina será chamada também no evento Click do botão Salvar)
Poderíamos melhorar a rotina preencheTabelas pois ela preenche ambas as tabelas ( clientes e estados) e neste caso preencher a tabela estados não é preciso. Deixo como trabalho para você escrever uma rotina separada para preencher cada tabela.
A seguir o código :
Private Sub recarregaDataSetClientes(ByVal strNome As String) 'limpa e recarrega o dataset clientes chamando a rotina para criar os objetos command e prencher o datasets dsClientes.Tables("Clientes").Clear() IniciaObjetos() preencheTabelas() 'reinicia a combobos para o registro clientes cboClientes.Text = strNome Me.Cursor = Windows.Forms.Cursors.Arrow End Sub |
O código do botão Salvar esta exibido abaixo. Nele estamos validando os dados invocando a rotina ValidaDados() antes de salvar ; encerrando a edição e atualizando o dataadapter. O tratamento de exceções permite capturar diversos tipos de possíveis exceções.
Private Sub btnSalvar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSalvar.Click Try If validaDados() Then Dim strClienteNomeAtual As String = txtNome.Text bmClientes.EndCurrentEdit() daClientes.Update(dsClientes, "Clientes") If blnNovaLinha Then blnNovaLinha = False End If defineBotoes(True) travaCaixaTextos(True) cboClientes.Focus() End If Catch eConstraint As ConstraintException 'ocorreu um erro nos valores dos dados; o dataset é recarregado MessageBox.Show("Ocorreu um erro : " & ControlChars.NewLine & eConstraint.Message, "Erro no valos dos dados", _ MessageBoxButtons.OK, MessageBoxIcon.Error) Catch eConcorrencia As DBConcurrencyException 'ocorreu um erro de concorrência na exclusão dos registros MessageBox.Show("Ocorreu um erro : " & ControlChars.NewLine & eConcorrencia.Message, "Erro na exclusão do registro.", _ MessageBoxButtons.OK, MessageBoxIcon.Error) Catch eNulo As NoNullAllowedException 'um valor nulo para um dado requerido MessageBox.Show("Ocorreu um erro : " & ControlChars.NewLine & eNulo.Message, "erro ADO.NET.", _ MessageBoxButtons.OK, MessageBoxIcon.Error) Catch eDados As DataException 'ocorreu um erro ADO.NET MessageBox.Show("Ocorreu um erro : " & ControlChars.NewLine & eDados.Message, "erro ADO.NET.", _ MessageBoxButtons.OK, MessageBoxIcon.Error) Catch eSistema As Exception 'ocorreu um erro genérico MessageBox.Show("Ocorreu um erro : " & ControlChars.NewLine & eSistema.Message, "erro ADO.NET.", _ MessageBoxButtons.OK, MessageBoxIcon.Error) Finally 'define o cursor do mouse e o foco Me.Cursor = Windows.Forms.Cursors.Arrow End Try End Sub
|
A função ValidaDados apenas verifica se alguns dados são informados e retorna True ou False.
Private Function validaDados() As Boolean Dim strErroMsg As String If txtNome.Text = "" Then strErroMsg = "Informe o Nome." txtNome.Focus() ElseIf txtCidade.Text = "" Then strErroMsg = "Informe a Cidade." txtCidade.Focus() ElseIf cboEstados.SelectedIndex = -1 Then strErroMsg = "Selecione um Estado." cboEstados.Focus() ElseIf txtCep.Text = "" Then strErroMsg = "O Cep é obrigatório." txtCep.Focus() End If If strErroMsg = "" Then validaDados = True Else validaDados = False MessageBox.Show(strErroMsg, "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error) End If End Function
|
Use o seu talento e para completar o projeto ...
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