Você está na página 1de 5

ADO.

NET

ADO.NET - Concorrência de dados


Por Leonardo Bruno

Concorrência de dados é um dos assuntos que mais são discutidos quando trabalhamos Download
com dados desconectados, e com o advento do ADO.NET, se tornou crucial para o Fontes da aplicação exemplo
desenvolvedor saber como lidar com essa situação. Veremos nesse artigo, algumas 108 KB
alternativas para tratar esse problema de forma segura e ao mesmo tempo elegante do
ponto de vista da interação com o usuário.

O que acontece se na hora que você tentar aplicar a sua alteração no banco de dados, e alguém já tenha modificado esse
mesmo registro? Tecnicamente falando, você tem um conflito de dados. Como tratar esse conflito é estritamente um
problema específico de cada aplicação, que pode ser bem resumido em três opções: First-win (A primeira alteração é que
prevalece), Last-win (A última alteração é que prevalece) e Ask-the-user (O usuário decide). Vejamos cada uma em detalhes,
lembrando que a abordagem que daremos aqui é a Optimistic Concurrency, a única que é aceita pelo ADO.NET.

First-win: O conflito é resolvido silenciosamente e automaticamente removendo a última alteração. Para implementar esse
método, você simplesmente define a propriedade ContinueUpdateError do DataAdapter como True. Sendo assim, nenhuma
exceção será disparada quando ocorrer esse tipo de situação. A informação sobre a exceção de cada linha que a causou é
armazenadas na propriedade RowError enquanto o processo de atualização continua a ocorrer para as demais linhas.

Last-win: Suas alterações são aplicadas independentes do status da linha. Para implementar esse modelo, você deve
simplesmente se assegurar que o seu comando SQL não seja muito restritivo, ou seja, se você criar um comando SQL que
atualize ou apague uma linha, pesquisando pela sua chave primária, nenhum conflito existirá.

Ask-the-user: Você pode dar a liberdade para o usuário escolher o que fazer. Basicamente escolher entre as duas opções
anteriores. Por default um conflito disparará uma DbConcurrencyException, a não ser que você tenha definido a propriedade
ContinueUpdateError como True. A propriedade Row da classe DbConcurrencyException, retorna a referência da linha que
causou a exceção e ter acesso ao valor original e atual da linha para lhe ajudar a dar mais informações para que o usuário
possa fazer sua escolha.

Planejando seu código para tratar concorrência de dados.

Vamos fazer um exemplo onde poderemos dar ao usuário a opção de escolha de como será o tratamento de concorrência.

Crie um novo projeto Windows Application, chame-o de ADOConcurrency e adicione os controles de acordo com a imagem e
descrição abaixo:

Object Propriedade Valor

Textbox Name txtPrimeiroNome

Text ""

Textbox Name txtUltimoNome

Text ""

Textbox Name txtCargo


Object Propriedade Valor

Text ""

Textbox Name txtNotas

MultiLine True

Text ""

Button Name btnVoltar

Text <

Button Name btnAvancar

Text >

Label Name lblPosicao

Backcolor Info

Text ""

TextAlign MiddleCenter

Form Name FrmFuncionarios

Text Funcionarios

Startup Position CenterScreen

FormBorderStyle FixedSingle

MaximizeBox False

GroupBox Name gbTrataConcor

Text Tratamento de concorrência

RadioButton Name rbFirstWin

Text First-win

RadioButton Name rbLastWin

Text Last-Win

RadioButton Name rbAskUser

Text Ask-user

Button Name btnUpdate

Text Update

Vá em Server Explorer e crie uma nova conexão com o banco de dados Northwind. Depois arraste a tabela de Funcionários
para o formulário. Veja que automaticamente será criado um objeto OleDbConnection1 e OleDbDataAdapter1. Agora, clique
com o botão direito em OleDbDataAdapter1 e escolha a opção Generate DataSet. Defina o nome do DataSet como
dsNorthwind e clique em OK. Sua barra de componentes deverá ficar assim:

Agora vamos ao código:

Declare as seguintes variáveis:

Dim RecordCount As Integer


Dim Position As Integer
'
'Enumeração para facilitar a escolha do tipo de tratamento de concorrência
'
Enum TipoTratamento
FirstWin = 0
LastWin = 1
Askuser = 2
End Enum
'
Dim TipoT As TipoTratamento = TipoTratamento.LastWin

No evento Load iremos preencher o DataSet, vincular os controles e definir um Event Handler para atulaização do Label com
a posição do registro.

Private Sub FrmFuncionarios_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles MyBase.Load
'
'Preenche o DataSet
'
Me.OleDbDataAdapter1.Fill(Me.DsNorthwind1)
'
'Vincula os controles
'
Me.txtPrimeiroNome.DataBindings.Add("Text", Me.DsNorthwind1.Funcionários, "Nome")
Me.txtUltimoNome.DataBindings.Add("Text", Me.DsNorthwind1.Funcionários, "Sobrenome")
Me.txtCargo.DataBindings.Add("Text", Me.DsNorthwind1.Funcionários, "Cargo")
Me.txtNotas.DataBindings.Add("Text", Me.DsNorthwind1.Funcionários, "Observações")
'
'Adiciona os Handles para o evento Click
'
AddHandler Me.btnAvancar.Click, AddressOf Validação
AddHandler Me.btnVoltar.Click, AddressOf Validação
'
AtualizaLabel()
'
End Sub

Na rotina de validação, teremos os tratamentos para uma correta navegação entre os registros:

Private Sub Validação(ByVal sender As System.Object, ByVal e As System.EventArgs)


'
'Obtem a quantidade de registros
'
RecordCount = Me.BindingContext(Me.DsNorthwind1.Funcionários).Count
'
'Obtem a posição atual
'
Position = Me.BindingContext(Me.DsNorthwind1.Funcionários).Position
'
'Verifica se existem registros
'
If RecordCount >= 1 Then
Me.btnAvancar.Enabled = False
Me.btnVoltar.Enabled = False
AtualizaLabel()
Exit Sub
End If
'
'Analisa de podemos voltar
'
If sender.Equals(Me.btnVoltar) Then
If RecordCount > 1 Then
Me.BindingContext(Me.DsNorthwind1.Funcionários).Position -= 1
Else
Me.btnVoltar.Enabled = False
End If
End If
'
'Analisa de podemos avançar
'
If sender.Equals(Me.btnAvancar) Then
If RecordCount > Position Then
Me.BindingContext(Me.DsNorthwind1.Funcionários).Position += 1
Else
Me.btnAvancar.Enabled = False
End If
End If
'
'Obtem a nova posição
'
Position = Me.BindingContext(Me.DsNorthwind1.Funcionários).Position + 1
'
'Recalcula os status dos botões de navegação
'
If RecordCount = Position Then
btnAvancar.Enabled = False
Else
btnAvancar.Enabled = True
End If

If Position = 1 Then
btnVoltar.Enabled = False
Else
btnVoltar.Enabled = True
End If
'
'Atualiza o label que mostra a posição atual
'
AtualizaLabel()
'
End Sub

Temos também a rotina que atualiza o Label com a informação da posição no registro:

Private Sub AtualizaLabel()


RecordCount = Me.BindingContext(Me.DsNorthwind1.Funcionários).Count
Position = Me.BindingContext(Me.DsNorthwind1.Funcionários).Position + 1

If RecordCount <= 1 Then


lblPosicao.Text = "Sem Registro"
Else
lblPosicao.Text = "Registro " & Position & " de " & RecordCount
End If
End Sub

Na rotina que trata o evento do botão Update decidimos a forma como tratar a concorrência de dados de acordo com a
escolha do usuário.

Private Sub btnUpdate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles btnUpdate.Click
Try
If TipoT = TipoTratamento.LastWin Then
AtualizarRegistro()
Exit Sub
ElseIf TipoT = TipoTratamento.FirstWin Then
Me.OleDbDataAdapter1.ContinueUpdateOnError = True
ElseIf TipoT = TipoTratamento.Askuser Then
Me.OleDbDataAdapter1.ContinueUpdateOnError = False
End If
'
Me.BindingContext(Me.DsNorthwind1.Funcionários).EndCurrentEdit()
Me.OleDbDataAdapter1.Update(Me.DsNorthwind1.GetChanges)
'
Catch Ex As Exception
MessageBox.Show(Ex.Message)
End Try
End Sub

No evento RowUpdated, perguntamos ao usuário o que ele deseja fazer, caso ocorra um erro de concorrência na atualização.

Private Sub OleDbDataAdapter1_RowUpdated(ByVal sender As Object,


ByVal e As System.Data.OleDb.OleDbRowUpdatedEventArgs) Handles OleDbDataAdapter1.RowUpdated
If TipoT = TipoTratamento.Askuser Then
If e.Status = UpdateStatus.ErrorsOccurred Then
If MessageBox.Show("O registro do funcionário " & e.Row.Item("Nome") & " foi
modificado desde a última vez que foi obtido " & Environment.NewLine &
"Deseja sobrescrever?", Me.Text, MessageBoxButtons.YesNo) = DialogResult.Yes Then
AtualizarRegistro()
e.Status = UpdateStatus.Continue
Else
e.Status = UpdateStatus.Continue
End If
End If
End If
End Sub
Private Sub rb_CheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs)
Handles rbAskUser.CheckedChanged, rbFirstWin.CheckedChanged, rbLastWin.CheckedChanged
If CType(sender, RadioButton).Name = "rbFirstWin" Then
TipoT = TipoTratamento.FirstWin
ElseIf CType(sender, RadioButton).Name = "rbLastWin" Then
TipoT = TipoTratamento.LastWin
ElseIf CType(sender, RadioButton).Name = "rbAskUser" Then
TipoT = TipoTratamento.Askuser
End If
End Sub

Na rotina AtualizarRegistro, fazemos a atualização com base na chave da tabela.

Private Sub AtualizarRegistro()


Try
'
'Atualização manual, com base na chave da tabela
'
Dim oComm As New OleDb.OleDbCommand("UPDATE Funcionários SET Nome = ?, Sobrenome = ?,
Cargo = ?, Observações = ? WHERE CódigoDoFuncionário = ?", Me.OleDbConnection1)

oComm.Parameters.Add("Nome", Me.txtPrimeiroNome.Text)
oComm.Parameters.Add("Sobrenome", Me.txtUltimoNome.Text)
oComm.Parameters.Add("Cargo", Me.txtCargo.Text)
oComm.Parameters.Add("Observações", Me.txtNotas.Text)
oComm.Parameters.Add("CódigoDoFuncionário", Me.lblCodigo.Text)

If Me.OleDbConnection1.State = ConnectionState.Closed Then Me.OleDbConnection1.Open()


oComm.ExecuteNonQuery()

Catch Ex As Exception
'
Finally
Me.OleDbConnection1.Close()
End Try
End Sub

Início da página

Conclusão:
Vimos um exemplo bem simples, mas que possui a essência do esquema de tratamento de concorrência com ADO.NET. Para
qualquer dúvida ou comentário estarei à disposição. Até a próxima.

Leonardo Bruno
lblima_net@hotmail.com
Currículo: Most Valuable Professional 2004 [Visual Basic .NET]. Trabalha com desenvolvimento de aplicações .NET desde
2001. Ministra cursos sobre a plataforma .NET, é consultor de tecnologia e desenvolvedor de sistemas na RR Consultoria e
Sistemas (Fortaleza - CE). Atualmente está dedicado ao desenvolvimento de um sistema ERP utilizando a plataforma .NET

Início da página

Fale Conosco | Imprima esta página | Adicione aos Favoritos

©2006 Microsoft Corporation. Todos os direitos reservados. Nota Legal | Marcas comerciais | Política de Privacidade

Você também pode gostar