Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Aplica-se ao: Access 2013, Office 2013
Detectando e resolvendo conflitos
Se você estiver lidando com o Recordset no modo imediato, a probabilidade de ocorrerem problemas de simultaneidade é bem menor. Por outro lado, se o aplicativo usar atualização no modo em lotes, é bem provável que um usuário altere um registro antes que as alterações feitas por outro usuário que está editando o mesmo registro sejam salvas. Nesse caso, você desejará que o aplicativo trate o conflito habilmente. Pode ser seu desejo que a última pessoa a enviar uma atualização para o servidor "ganhe". Ou você pode querer permitir que o usuário mais recente decida qual atualização deve ter precedência, fornecendo-lhe uma escolha entre os dois valores conflitantes.
Seja qual for o caso, o ADO fornece as propriedades UnderlyingValue e OriginalValue do objeto Field para tratar esses tipos de conflitos. Use-as em combinação com o método Resync e a propriedade Filter do Recordset.
Detectando erros
Quando o ADO encontra um conflito durante uma atualização em lotes, um aviso é colocado na coleção Errors. Portanto, você deve sempre verificar se há erros, imediatamente após chamar BatchUpdate e, se encontrá-los, comece testando a suposição de que você encontrou um conflito. A primeira etapa é definir a propriedade Filter no Recordset como adFilterConflictingRecords (a propriedade Filter é discutida no capítulo anterior). Isso limita a exibição do Recordset a apenas aqueles registros que estão em conflito. Se a propriedade RecordCount for igual a zero após essa etapa, você saberá que o erro foi causado por outro motivo que não um conflito.
Quando você chama BatchUpdate, o ADO e o provedor estão gerando instruções SQL para executar atualizações na fonte de dados. Lembre-se de que determinadas fontes de dados têm limitações quanto aos tipos de colunas que podem ser usados em uma cláusula WHERE.
Em seguida, chame o método Resync no Recordset com o argumento AffectRecords definido como adAffectGroup e o argumento ResyncValues definido como adResyncUnderlyingValues. O método Resync atualiza os dados no objeto Recordset atual a partir do banco de dados subjacente. Usando adAffectGroup, você garante que apenas os registros visíveis com a configuração de filtro atual, ou seja, apenas os registros em conflito, sejam resincronizados com o banco de dados. Isso pode fazer uma diferença significativa no desempenho, se você estiver lidando com um Recordset grande. Definindo o argumento ResyncValues como adResyncUnderlyingValues ao chamar Resync, você garante que a propriedade UnderlyingValue conterá o valor (conflitante) do banco de dados, que a propriedade Value manterá o valor inserido pelo usuário e que a propriedade OriginalValue manterá o valor original do campo (o valor que ele tinha antes que a última chamada bem-sucedida para UpdateBatch fosse feita). Em seguida, você poderá usar esses valores para resolver o conflito de maneira programática ou deixar que o usuário escolha o valor que será usado.
Essa técnica é mostrada no exemplo de código abaixo. O exemplo cria artificialmente um conflito usando um Recordset separado para alterar um valor na tabela subjacente antes que UpdateBatch seja chamado.
'BeginConflicts
On Error GoTo ErrHandler:
Dim objRs1 As New ADODB.Recordset
Dim objRs2 As New ADODB.Recordset
Dim strSQL As String
Dim strMsg As String
strSQL = "SELECT * FROM Shippers WHERE ShipperID = 2"
'Open Rs and change a value
objRs1.CursorLocation = adUseClient
objRs1.Open strSQL, strConn, adOpenStatic, adLockBatchOptimistic, adCmdText
objRs1("Phone") = "(111) 555-1111"
'Introduce a conflict at the db...
objRs2.Open strSQL, strConn, adOpenKeyset, adLockOptimistic, adCmdText
objRs2("Phone") = "(999) 555-9999"
objRs2.Update
objRs2.Close
Set objRs2 = Nothing
On Error Resume Next
objRs1.UpdateBatch
If objRs1.ActiveConnection.Errors.Count <> 0 Then
Dim intConflicts As Integer
intConflicts = 0
objRs1.Filter = adFilterConflictingRecords
intConflicts = objRs1.RecordCount
'Resync so we can see the UnderlyingValue and offer user a choice.
'This sample only displays all three values and resets to original.
objRs1.Resync adAffectGroup, adResyncUnderlyingValues
If intConflicts > 0 Then
strMsg = "A conflict occurred with updates for " & intConflicts & _
" record(s)." & vbCrLf & "The values will be restored" & _
" to their original values." & vbCrLf & vbCrLf
objRs1.MoveFirst
While Not objRs1.EOF
strMsg = strMsg & "SHIPPER = " & objRs1("CompanyName") & vbCrLf
strMsg = strMsg & "Value = " & objRs1("Phone").Value & vbCrLf
strMsg = strMsg & "UnderlyingValue = " & _
objRs1("Phone").UnderlyingValue & vbCrLf
strMsg = strMsg & "OriginalValue = " & _
objRs1("Phone").OriginalValue & vbCrLf
strMsg = strMsg & vbCrLf & "Original value has been restored."
MsgBox strMsg, vbOKOnly, _
"Conflict " & objRs1.AbsolutePosition & _
" of " & intConflicts
objRs1("Phone").Value = objRs1("Phone").OriginalValue
objRs1.MoveNext
Wend
objRs1.UpdateBatch adAffectGroup
Else
'Other error occurred. Minimal handling in this example.
strMsg = "Errors occurred during the update. " & _
objRs1.ActiveConnection.Errors(0).Number & " " & _
objRs1.ActiveConnection.Errors(0).Description
End If
On Error GoTo 0
End If
objRs1.MoveFirst
'Clean up
objRs1.Close
Set objRs1 = Nothing
Exit Sub
ErrHandler:
If Not objRs1 Is Nothing Then
If objRs1.State = adStateOpen Then objRs1.Close
Set objRs1 = Nothing
End If
If Not objRs2 Is Nothing Then
If objRs2.State = adStateOpen Then objRs2.Close
Set objRs2 = Nothing
End If
If Err <> 0 Then
MsgBox Err.Source & "-->" & Err.Description, , "Error"
End If
'EndConflicts
Você pode usar a propriedade Status do Registro atual ou de um Campo específico para determinar o tipo de conflito que ocorreu.
Para obter informações mais detalhadas sobre o tratamento de erros, consulte Capítulo 6: Tratamento de erros.