Partager via


Détection et résolution des conflits

Si vous traitez votre Recordset en mode immédiat, les problèmes d’accès concurrentiel sont beaucoup moins probables. En revanche, si votre application utilise la mise à jour en mode par lot, il est possible qu'un utilisateur modifie un enregistrement avant que les modifications apportées par un autre utilisateur sur le même enregistrement soient enregistrées. 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 envoie une mise à jour au serveur « wins ». 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 du Recordset.

Remarques

Lorsque ADO rencontre un conflit lors d’une mise à jour par lots, un avertissement est ajouté à la collection Errors. Par conséquent, vous devez toujours rechercher des erreurs immédiatement après avoir appelé BatchUpdate et, si vous les trouvez, commencez à tester l’hypothèse que vous avez rencontré un conflit. La première étape consiste à définir la propriété Filter du Recordset égale à adFilterConflictingRecords. Cela limite l’affichage de votre recordset à ceux qui sont en conflit uniquement. Si la propriété RecordCount est égale à zéro après cette étape, vous savez que l’erreur a été généré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 qui peuvent ê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 défini sur 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 assurez que seuls les enregistrements visibles avec le paramètre de filtre actuel, autrement dit, seuls 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 d’un jeu d’enregistrements 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 (conflictuelle) 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 contiendra la valeur d’origine du champ (la valeur qu’elle avait avant la dernière réussite de l’appel UpdateBatch a été effectuée). Vous pouvez ensuite utiliser ces valeurs pour résoudre le conflit par programme 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 de 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