Vous êtes sur la page 1sur 5

ADO.

NET

ADO.NET - Concorrncia de dados


Por Leonardo Bruno
Concorrncia de dados um dos assuntos que mais so discutidos quando trabalhamos
com dados desconectados, e com o advento do ADO.NET, se tornou crucial para o
desenvolvedor saber como lidar com essa situao. Veremos nesse artigo, algumas
alternativas para tratar esse problema de forma segura e ao mesmo tempo elegante do
ponto de vista da interao com o usurio.

Download
Fontes da aplicao exemplo
108 KB

O que acontece se na hora que voc tentar aplicar a sua alterao no banco de dados, e algum j tenha modificado esse
mesmo registro? Tecnicamente falando, voc tem um conflito de dados. Como tratar esse conflito estritamente um
problema especfico de cada aplicao, que pode ser bem resumido em trs opes: First-win (A primeira alterao que
prevalece), Last-win (A ltima alterao que prevalece) e Ask-the-user (O usurio 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 alterao. Para implementar esse
mtodo, voc simplesmente define a propriedade ContinueUpdateError do DataAdapter como True. Sendo assim, nenhuma
exceo ser disparada quando ocorrer esse tipo de situao. A informao sobre a exceo de cada linha que a causou
armazenadas na propriedade RowError enquanto o processo de atualizao continua a ocorrer para as demais linhas.
Last-win: Suas alteraes so aplicadas independentes do status da linha. Para implementar esse modelo, voc deve
simplesmente se assegurar que o seu comando SQL no seja muito restritivo, ou seja, se voc criar um comando SQL que
atualize ou apague uma linha, pesquisando pela sua chave primria, nenhum conflito existir.
Ask-the-user: Voc pode dar a liberdade para o usurio escolher o que fazer. Basicamente escolher entre as duas opes
anteriores. Por default um conflito disparar uma DbConcurrencyException, a no ser que voc tenha definido a propriedade
ContinueUpdateError como True. A propriedade Row da classe DbConcurrencyException, retorna a referncia da linha que
causou a exceo e ter acesso ao valor original e atual da linha para lhe ajudar a dar mais informaes para que o usurio
possa fazer sua escolha.
Planejando seu cdigo para tratar concorrncia de dados.
Vamos fazer um exemplo onde poderemos dar ao usurio a opo de escolha de como ser o tratamento de concorrncia.
Crie um novo projeto Windows Application, chame-o de ADOConcurrency e adicione os controles de acordo com a imagem e
descrio abaixo:

Object

Propriedade

Valor

Textbox

Name

txtPrimeiroNome

Text

""

Name

txtUltimoNome

Text

""

Name

txtCargo

Textbox

Textbox

Object

Textbox

Button

Button

Label

Form

GroupBox

RadioButton

RadioButton

RadioButton

Button

Propriedade

Valor

Text

""

Name

txtNotas

MultiLine

True

Text

""

Name

btnVoltar

Text

<

Name

btnAvancar

Text

>

Name

lblPosicao

Backcolor

Info

Text

""

TextAlign

MiddleCenter

Name

FrmFuncionarios

Text

Funcionarios

Startup Position

CenterScreen

FormBorderStyle

FixedSingle

MaximizeBox

False

Name

gbTrataConcor

Text

Tratamento de concorrncia

Name

rbFirstWin

Text

First-win

Name

rbLastWin

Text

Last-Win

Name

rbAskUser

Text

Ask-user

Name

btnUpdate

Text

Update

V em Server Explorer e crie uma nova conexo com o banco de dados Northwind. Depois arraste a tabela de Funcionrios
para o formulrio. Veja que automaticamente ser criado um objeto OleDbConnection1 e OleDbDataAdapter1. Agora, clique
com o boto direito em OleDbDataAdapter1 e escolha a opo Generate DataSet. Defina o nome do DataSet como
dsNorthwind e clique em OK. Sua barra de componentes dever ficar assim:

Agora vamos ao cdigo:

Declare as seguintes variveis:


Dim RecordCount As Integer
Dim Position As Integer
'
'Enumerao para facilitar a escolha do tipo de tratamento de concorrncia
'
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 atulaizao do Label com
a posio 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.Funcionrios, "Nome")
Me.txtUltimoNome.DataBindings.Add("Text", Me.DsNorthwind1.Funcionrios, "Sobrenome")
Me.txtCargo.DataBindings.Add("Text", Me.DsNorthwind1.Funcionrios, "Cargo")
Me.txtNotas.DataBindings.Add("Text", Me.DsNorthwind1.Funcionrios, "Observaes")
'
'Adiciona os Handles para o evento Click
'
AddHandler Me.btnAvancar.Click, AddressOf Validao
AddHandler Me.btnVoltar.Click, AddressOf Validao
'
AtualizaLabel()
'
End Sub
Na rotina de validao, teremos os tratamentos para uma correta navegao entre os registros:

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


'
'Obtem a quantidade de registros
'
RecordCount = Me.BindingContext(Me.DsNorthwind1.Funcionrios).Count
'
'Obtem a posio atual
'
Position = Me.BindingContext(Me.DsNorthwind1.Funcionrios).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.Funcionrios).Position -= 1
Else
Me.btnVoltar.Enabled = False
End If
End If
'
'Analisa de podemos avanar
'
If sender.Equals(Me.btnAvancar) Then
If RecordCount > Position Then
Me.BindingContext(Me.DsNorthwind1.Funcionrios).Position += 1
Else
Me.btnAvancar.Enabled = False
End If
End If
'
'Obtem a nova posio
'
Position = Me.BindingContext(Me.DsNorthwind1.Funcionrios).Position + 1

'
'Recalcula os status dos botes de navegao
'
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 posio atual
'
AtualizaLabel()
'
End Sub
Temos tambm a rotina que atualiza o Label com a informao da posio no registro:

Private Sub AtualizaLabel()


RecordCount = Me.BindingContext(Me.DsNorthwind1.Funcionrios).Count
Position = Me.BindingContext(Me.DsNorthwind1.Funcionrios).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 boto Update decidimos a forma como tratar a concorrncia de dados de acordo com a
escolha do usurio.

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.Funcionrios).EndCurrentEdit()
Me.OleDbDataAdapter1.Update(Me.DsNorthwind1.GetChanges)
'
Catch Ex As Exception
MessageBox.Show(Ex.Message)
End Try
End Sub
No evento RowUpdated, perguntamos ao usurio o que ele deseja fazer, caso ocorra um erro de concorrncia na atualizao.

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 funcionrio " & 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 atualizao com base na chave da tabela.

Private Sub AtualizarRegistro()


Try
'
'Atualizao manual, com base na chave da tabela
'
Dim oComm As New OleDb.OleDbCommand("UPDATE Funcionrios SET Nome = ?, Sobrenome = ?,
Cargo = ?, Observaes = ? WHERE CdigoDoFuncionrio = ?", 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("Observaes", Me.txtNotas.Text)
oComm.Parameters.Add("CdigoDoFuncionrio", 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
Incio da pgina

Concluso:
Vimos um exemplo bem simples, mas que possui a essncia do esquema de tratamento de concorrncia com ADO.NET. Para
qualquer dvida ou comentrio estarei disposio. At a prxima.
Leonardo Bruno
lblima_net@hotmail.com
Currculo: Most Valuable Professional 2004 [Visual Basic .NET]. Trabalha com desenvolvimento de aplicaes .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

Incio da pgina

Fale Conosco | Imprima esta pgina | Adicione aos Favoritos


2006 Microsoft Corporation. Todos os direitos reservados. Nota Legal | Marcas comerciais | Poltica de Privacidade