Partager via


Détection et résolution des conflits

S’applique à : Access 2013, Office 2013

Détection et résolution des conflits

Si vous traitez votre recordset en mode immédiat, il y a beaucoup moins de chances que des problèmes d’accès concurrentiel surviennent. En revanche, si votre application utilise la mise à jour en mode batch, il peut y avoir de bonnes chances qu’un utilisateur modifie un enregistrement avant que les modifications apportées par un autre utilisateur modifiant le même enregistrement ne soient enregistrées. Dans ce cas, vous souhaiterez que votre application gère le conflit de manière appropriée. Il se peut que vous souhaitiez que la dernière personne à envoyer une mise à jour au serveur « gagne ». Vous pouvez également laisser l’utilisateur le plus récent décider quelle mise à jour doit être prioritaire en lui donnant le choix entre les deux valeurs en conflit.

Quoi qu'il en soit, ADO fournit les propriétés UnderlyingValue et OriginalValue de l'objet Field afin de gérer ces types de conflits. Utilisez ces propriétés avec la méthode Resync et la propriété Filter de l'objet Recordset.

Détection des erreurs

En cas de conflit détecté par ADO au cours d'une mise à jour par lot, un avertissement est placé dans la collection Errors. Par conséquent, vous devez toujours contrôler les erreurs immédiatement après un appel de BatchUpdate, et si vous en trouvez, vous devez vérifier s'il s'agit effectivement d'un conflit. Vous devez d'abord affecter à la propriété Filter de l'objet Recordset la valeur adFilterConflictingRecords (la propriété Filter est décrite dans le chapitre précédent). De cette façon, seuls les enregistrements conflictuels sont affichés dans votre objet Recordset. Si la propriété RecordCount est égale à zéro une fois cette étape franchie, vous pouvez en déduire que la cause de l'erreur n'est pas un conflit.

Lorsque vous appelez BatchUpdate, ADO et le fournisseur génèrent des instructions SQL pour effectuer des mises à jour sur la source de données. N'oubliez pas que certaines sources de données possèdent des restrictions quant aux types de colonnes autorisés dans une clause WHERE.

Vous devez ensuite appeler la méthode Resync sur l'objet Recordset avec l'argument AffectRecords égal à adAffectGroup et l'argument ResyncValues égal à adResyncUnderlyingValues. La méthode Resync actualise les données dans l'objet Recordset actif à partir de la base de données sous-jacente. Si vous utilisez adAffectGroup, seuls les enregistrements visibles avec le paramètre de filtre actuel, à savoir les enregistrements présentant un conflit, sont resynchronisés avec la base de données. Ceci permet d'améliorer sensiblement les performances lorsque vous travaillez avec un objet Recordset volumineux. En affectant à l'argument ResyncValues la valeur adResyncUnderlyingValues lors d'un appel de Resync, la propriété UnderlyingValue contiendra la valeur (conflictuelle) de la base de données, la propriété Value gèrera la valeur entrée par l'utilisateur et la propriété OriginalValue conservera la valeur initiale du champ (à savoir, la valeur du champ avant le dernier appel réussi de UpdateBatch). Vous pouvez alors utiliser ces valeurs pour résoudre le conflit par programme ou demander à l'utilisateur de choisir la valeur à utiliser.

Cette technique est illustrée dans l'exemple de code suivant. Un conflit y est artificiellement créé en utilisant un objet Recordset distinct pour modifier une valeur dans la table sous-jacente avant l'appel de 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 

Vous pouvez utiliser la propriété Status de l'objet Record actif ou d'un objet Field spécifique pour déterminer le type de conflit en cours.

Pour plus d'informations sur la gestion des erreurs, consultez le chapitre 6 : Gestion des erreurs.