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 com dados desconectados, e com o advento do ADO.NET, se tornou crucial para o desenvolvedor saber como lidar com essa situação. Veremos nesse artigo, algumas alternativas para tratar esse problema de forma segura e ao mesmo tempo elegante do ponto de vista da interação com o usuário.

  Download Fontes da aplicação exemplo 108 KB
  Download Fontes da aplicação exemplo 108 KB
 

Download

Fontes da aplicação exemplo 108 KB

Fontes da aplicação exemplo 108 KB

Fontes da aplicação exemplo 108 KB

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:

os controles de acordo com a imagem e descrição abaixo: Object Propriedade Valor Textbox Name

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:

clique em OK. Sua barra de componentes deverá ficar assim: Agora vamos ao código: Declare as

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' Finally Me.OleDbConnection1.Close() End Try End Sub Conclusão: Vimos um exemplo bem simples, mas que possui

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áginade um sistema ERP utilizando a plataforma .NET Fale Conosco | Imprima esta página | Adicione

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

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

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