Atualizando DataSets em camadas

Imagine um sistema windows simples, em camadas. Imaginemos uma tabela simples com um campo autonumeração sendo exibida em uma grid. Um componente (pode ser uma classe, componente, etc) devolve um dataSet contendo os dados da tabela e recebe de volta o dataSet para fazer a atualização no servidor.

Esse trabalho em camadas gera diversos tipos de problemas, um dos principais é a recuperação do id da autonumeração. Em algumas situações complexas a autonumeração pode chegar a causar um grande desencontro. Que o valor do campo autonumeração inserido no dataSet não baterá com o valor do banco, isso é esperado, mas existe a possibilidade de que o valor gerado pelo banco entre em conflito com algum valor já existente no DataSet, gerando um grande problema.

Para evitar que ocorra um conflito tão crítico podemos utilizar as propriedades AutoIncrementSeed e AutoIncrementStep. São propriedades do objeto dataColumn, podemos encontra-las dentro do editor de um dataSet tipado. Atribuindo -1 e -1, respectivamente, faremos com que a autonumeração no dataSet seja negativa. Ao fazer a atualização no servidor o número do servidor será atualizado no dataset, mas não conflitará com nenhum outro número que o dataset já possua.

Veja alguns passos no processo de atualização:

  1. O client precisa enviar os dados a serem atualizados para o servidor. Não deve enviar tudo e sim apenas os dados modificados. Por isso neste momento deve-se utilizar o método getChanges para obter uma cópia do dataset apenas com as mudanças;
  2. Vamos considerar uma atualização não transacional. Neste caso configuramos a propriedade continueupdateonerror do dataAdapter para true para que ele não devolva erros, apenas marque os registros que gerarem erro de atualização;
  3. O método de atualização precisa devolver o dataset após a atualização. Isso porque o client está com o dataset inteiro e o client usará o dataset devolvido para saber quais atualizações funcionaram ou não;
  4. O adapter, ao fazer o método update, tipicamente realiza também um acceptchanges no registro. Porém no caso de inserções isso não pode ser feito por causa da autonumeração. Se fosse feito a mesclagem do dataSet no client falharia, duplicando registros. Então precisamos tratar o evento rowUpdated do adapter para garantir que não seja feito o acceptChanges nos registros inseridos;
  5. O client pode, após a mesclagem do dataset, fazer um rejectChanges, para imediatamente rejeitar os dados dos registros que falharam na atualização, mas isso é opcional.
Veja o exemplo: Eis o client:

        Dim dados As WindowsControlLibrary1.DS

        #Region " Windows Form Designer generated code "

        Public Sub New()

        MyBase.New()

        This call is required by the Windows Form Designer.

        InitializeComponent()

        Add any initialization after the InitializeComponent() call

        End Sub

        Form overrides dispose to clean up the component list.

        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

        If disposing Then

        If Not (components Is Nothing) Then

        components.Dispose()

        End If

        End If

        MyBase.Dispose(disposing)

        End Sub

        Required by the Windows Form Designer

        Private components As System.ComponentModel.IContainer

        // NOTE: The following procedure is required by the Windows Form Designer

        // It can be modified using the Windows Form Designer.

        // Do not modify it using the code editor.

        Friend WithEvents DG As System.Windows.Forms.DataGrid

        Friend WithEvents Component11 As WindowsControlLibrary1.Component1

        Friend WithEvents Button1 As System.Windows.Forms.Button

        <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

        Me.components = New System.ComponentModel.Container

        Me.DG = New System.Windows.Forms.DataGrid

        Me.Component11 = New WindowsControlLibrary1.Component1(Me.components)

        Me.Button1 = New System.Windows.Forms.Button

        CType(Me.DG, System.ComponentModel.ISupportInitialize).BeginInit()

        Me.SuspendLayout()

        DG

        Me.DG.DataMember = ""

        Me.DG.HeaderForeColor = System.Drawing.SystemColors.ControlText

        Me.DG.Location = New System.Drawing.Point(16, 16)

        Me.DG.Name = "DG"

        Me.DG.Size = New System.Drawing.Size(256, 184)

        Me.DG.TabIndex = 0

        Button1

        Me.Button1.Location = New System.Drawing.Point(104, 224)

        Me.Button1.Name = "Button1"

        Me.Button1.TabIndex = 1

        Me.Button1.Text = "Button1"

        Form1

        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)

        Me.ClientSize = New System.Drawing.Size(292, 273)

        Me.Controls.Add(Me.Button1)

        Me.Controls.Add(Me.DG)

        Me.Name = "Form1"

        Me.Text = "Form1"

        CType(Me.DG, System.ComponentModel.ISupportInitialize).EndInit()

        Me.ResumeLayout(False)

        End Sub

        #End Region

        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        dados = Component11.ler

        DG.DataSource = dados.numeros

        End Sub

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        If dados.HasChanges Then

        Dim d As WindowsControlLibrary1.DS

        d = dados.GetChanges

        d = Component11.gravar(d)

        dados.Merge(d)

        dados.RejectChanges()

        Else

        MsgBox("nada foi mudado")

        End If

        End Sub

        
Eis o componente :

        Public Function ler() As DS

        DA.Fill(Ds1)

        Return (Ds1)

        End Function

        Public Function gravar(ByVal xx As DS) As DS

        DA.Update(xx)

        Return (xx)

        End Function

        Private Sub DA_RowUpdated(ByVal sender As Object, ByVal e As System.Data.OleDb.OleDbRowUpdatedEventArgs) Handles
        DA.RowUpdated

        If e.StatementType = StatementType.Select Then

        e.Status = UpdateStatus.SkipCurrentRow

        End If

        End Sub
        

Confira também