Compartir a través de


Detección y resolución de conflictos

Se aplica a: Access 2013, Office 2013

Detectar y resolver conflictos

Si trabaja con un conjunto de registros en modo inmediato, las posibilidades de que se produzcan problemas de simultaneidad son mucho menores. Por otra parte, si la aplicación utiliza el modo de actualización por lotes, es muy probable que un usuario cambie un registro antes de que se hayan guardado los cambios realizados por otro usuario que esté editando el mismo registro. En tal caso, deseará que la aplicación pueda ocuparse de resolver el conflicto sin más problemas. Puede ser su deseo que la última persona que envíe una actualización al servidor "gane". O bien, puede permitir que el usuario más reciente decida qué actualización debe tener prioridad si le proporciona una opción entre los dos valores en conflicto.

En cualquier caso, ADO proporciona las propiedades UnderlyingValue y OriginalValue del objeto Field para controlar estos tipos de conflictos. Utilice estas propiedades en combinación con el método Resync y la propiedad Filter del conjunto de registros.

Detectar errores

Cuando ADO detecta un conflicto durante una actualización por lotes, coloca una advertencia en la colección Errors. Por lo tanto, debe comprobar siempre si hay errores inmediatamente después de llamar a BatchUpdate y, si se observan, empiece por probar la suposición de que se ha producido un conflicto. El primer paso es establecer la propiedad Filter en el conjunto de registros como igual a adFilterConflictingRecords (la propiedad Filter se describe en el capítulo anterior). Esto limita la visión en el conjunto de registros a sólo los registros que están en conflicto. Si la propiedad RecordCount es igual a cero después de este paso, sabrá que el error se debe a una causa que no es un conflicto.

Al llamar a BatchUpdate, ADO y el proveedor generan instrucciones SQL para realizar actualizaciones en el origen de datos. Recuerde que ciertos orígenes de datos tienen limitaciones en cuanto a los tipos de columnas que se pueden utilizar en una cláusula WHERE.

A continuación, llame al método Resync en el conjunto de registros con el argumento AffectRecords establecido como igual a adAffectGroup y el argumento ResyncValues establecido como igual a adResyncUnderlyingValues. El método Resync actualiza los datos del objeto Recordset activo de la base de datos subyacente. Si utiliza adAffectGroup, se asegurará de que sólo se resincronicen con la base de datos los registros visibles con el valor de filtro actual, es decir, sólo los registros en conflicto. Esto podría suponer una diferencia de rendimiento considerable si se está trabajando con un conjunto de registros grande. Si establece el argumento ResyncValues en adResyncUnderlyingValues al llamar a Resync, se asegurará de que la propiedad UnderlyingValue contenga el valor (conflictivo) de la base de datos, que la propiedad Valor mantenga el valor que ha introducido el usuario y que la propiedad OriginalValue contenga el valor original del campo (el valor que tenía antes de realizarse la última llamada correcta a UpdateBatch). Posteriormente, puede utilizar estos valores para resolver el conflicto mediante programación o solicitar al usuario que elija el valor que se usará.

Esta técnica se muestra en el código de ejemplo siguiente. El ejemplo crea artificialmente un conflicto utilizando un conjunto de registros distinto para cambiar un valor en la tabla subyacente antes de llamar a UpdateBatch.

 
'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 

Puede utilizar la propiedad Status del registro activo o un campo específico para determinar qué tipo de conflicto se ha producido.

Para obtener información más detallada sobre el tratamiento de errores, vea Capítulo 6: Tratamiento de errores.