Partager via


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 se produisent. En revanche, si votre application utilise la mise à jour en mode batch, il se peut qu’un utilisateur modifie un enregistrement avant que les modifications apportées par un autre utilisateur modifient le même enregistrement. Dans ce cas, vous souhaiterez que votre application gère correctement le conflit. Il peut s’agir de votre souhait que la dernière personne qui envoie 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 fournissant un choix entre les deux valeurs en conflit.

Quel que soit le cas, ADO fournit les propriétés UnderlyingValue et OriginalValue de l’objet Field pour gérer ces types de conflits. Utilisez ces propriétés en combinaison avec la méthode Resync et la propriété Filter de l’objet Recordset.

Notes

Lorsqu’ADO rencontre un conflit lors d’une mise à jour par lot, un avertissement est ajouté à la collection Errors. Par conséquent, vous devez toujours vérifier les erreurs immédiatement après avoir appelé BatchUpdate ; si vous en trouvez, commencez à tester l’hypothèse que vous avez rencontré un conflit. La première étape consiste à définir la propriété Filter sur l’objet Recordset égal à adFilterConflictingRecords. Cela limite l’affichage sur votre Recordset aux enregistrements qui sont en conflit. Si la propriété RecordCount est égale à zéro après cette étape, vous savez que l’erreur a été déclenchée par quelque chose d’autre qu’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 ont des limitations sur les types de colonnes pouvant être utilisés dans une clause WHERE.

Ensuite, appelez la méthode Resync sur l’objet Recordset avec l’argument AffectRecords défini sur adAffectGroup et l’argument ResyncValues égal à adResyncUnderlyingValues. La méthode Resync met à jour les données de l’objet Recordset actif à partir de la base de données sous-jacente. En utilisant adAffectGroup, vous vous assurez que seuls les enregistrements visibles avec le paramètre de filtre actuel, autrement dit les enregistrements en conflit, sont resynchronisés avec la base de données. Cela peut faire une différence significative en matière de performances si vous traitez un Recordset volumineux. En définissant l’argument ResyncValues sur adResyncUnderlyingValues lors de l’appel de Resync, vous assurez que la propriété UnderlyingValue contient la valeur (en conflit) de la base de données, que la propriété Value conserve la valeur entrée par l’utilisateur et que la propriété OriginalValue conserve la valeur d’origine du champ (la valeur qu’elle avait avant la dernière réussite de l’appel à UpdateBatch). Vous pouvez ensuite utiliser ces valeurs pour résoudre le conflit par programmation ou exiger que l’utilisateur sélectionne la valeur qui sera utilisée.

Cette technique est illustrée dans l’exemple de code suivant. L’exemple crée artificiellement un conflit à l’aide d’un Recordset distinct pour modifier une valeur dans la table sous-jacente avant l’appel à UpdateBatch.

'BeginConflicts  
    strConn = "Provider=SQLOLEDB;Initial Catalog=Northwind;" & _  
              "Data Source=MySQLServer;Integrated Security=SSPI;"  
  
    strSQL = "SELECT * FROM Shippers WHERE ShipperID = 2"  
  
    'Open Rs and change a value  
    Set objRs1 = New ADODB.Recordset  
    Set objRs2 = New ADODB.Recordset  
    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  
    objRs1.Close  
    Set objRs1 = Nothing  
'EndConflicts  

Vous pouvez utiliser la propriété Status de l’enregistrement actif ou d’un champ spécifique pour déterminer le type de conflit qui s’est produit.

Pour plus d’informations sur la gestion des erreurs, consultez Gestion des erreurs.

Voir aussi

Mode Batch