Partager via


Gestion de conflits pour les fournisseurs simples

Si possible, concevez des applications de manière à éviter les conflits. La détection et la résolution de conflit introduisent une complexité, un traitement et un trafic réseau supplémentaires. Dans certaines applications, les conflits ne peuvent pas être évités. Par exemple, dans une application de force de vente, deux commerciaux peuvent partager un secteur de vente. Les deux commerciaux peuvent mettre à jour les données pour les mêmes client et commandes. Pour garantir la propagation correcte des modifications apportées aux éléments de la communauté de synchronisation, le fournisseur de destination doit détecter et gérer les conflits qui se produisent entre les éléments envoyés à partir du fournisseur de source et les éléments du réplica de destination. Sync Framework fournit des objets qui effectuent la majeure partie du travail requis pour détecter et gérer des conflits.

Sync Framework détecte des conflits au niveau de l'élément ou de l'unité de modification. Sync Framework reconnaît deux catégories de conflits qui peuvent se produire pendant la synchronisation : les conflits d'accès concurrentiel et les conflits de contraintes. Les conflits d'accès concurrentiel se produisent lorsque le même élément ou la même unité de modification est modifiée sur deux réplicas différents qui sont ensuite synchronisés. Les conflits de contraintes sont des conflits qui ne respectent pas les contraintes mises sur les éléments ou les unités de modification, par exemple la relation de dossiers ou l'emplacement de données portant le même nom dans un système de fichiers. Sync Framework divise les conflits de contraintes en trois types.

  • Un conflit de collision se produit lorsque l'élément ne peut pas être enregistré parce qu'il est en conflit avec un autre élément dans le magasin de destination, comme ce peut être le cas lorsque le fournisseur de source envoie un fichier portant le même nom et situé au même emplacement qu'un fichier qui existe déjà dans le réplica de destination.

  • Un conflit de parent manquant se produit lorsqu'un élément ne peut pas être enregistré dans un magasin de données hiérarchique parce qu'il requiert un élément parent qui n'existe pas, comme ce peut être le cas lorsque le fournisseur de source envoie un fichier à enregistrer dans un répertoire qui n'existe pas sur le réplica de destination.

  • D'autres conflits de contraintes se produisent lorsque l'élément à enregistrer ne respecte pas une contrainte du réplica de destination, comme ce peut être le cas lorsque le fournisseur de source envoie un fichier trop volumineux pour être enregistré sur le réplica de destination ou lorsque la modification va à l'encontre d'une logique métier sur le réplica de destination.

Les contraintes ont trait aux fonctions spécifiques d'un magasin d'éléments, comme les contraintes de clé étrangère, courantes dans les bases de données. Les fournisseurs simples ne prennent en charge que les conflits de contraintes de collision. Pour plus d'informations sur la gestion des conflits pour les fournisseurs personnalisés standard, consultez Détection et résolution des conflits de contraintes.

Présentation de la gestion des conflits

Pour décider de la façon de gérer des conflits d'accès concurrentiel et des conflits de contraintes, vous devez répondre à deux questions importantes :

  • Les conflits doivent-ils être résolus automatiquement pendant la synchronisation ou une notification doit-elle être envoyée à l'application pour que cette dernière puisse procéder à la résolution du conflit ?

  • Tous les conflits doivent-ils être résolus en spécifiant que la source ou la destination l'emporte ou une gestion des conflits plus sophistiquée est-elle requise ? Dans le cas d'un conflit d'accès concurrentiel, par exemple, vous pouvez fusionner les données sources et de destination en un élément unique qui s'applique aux deux réplicas.

Après avoir répondu à ces questions, vous pouvez spécifier la façon dont Sync Framework doit se comporter lorsqu'il rencontre un conflit :

  1. Spécifiez une stratégie de résolution pour les conflits d'accès concurrentiel et les conflits de contraintes de collision. Cette stratégie détermine si Sync Framework résout automatiquement le conflit ou si c'est l'application qui, par défaut, doit répondre à un événement pour gérer le conflit.

    Si vous spécifiez une stratégie autre que celle par défaut, Sync Framework définit l'action de résolution de conflit appropriée lorsqu'un conflit se produit. Par exemple, si vous spécifiez une stratégie « la source l'emporte » pour les conflits d'accès concurrentiel, l'action « la source l'emporte » est définie si un conflit de ce type est détecté pendant une session de synchronisation. Si vous acceptez la valeur par défaut pour l'une des stratégies de résolution ou les deux, le fournisseur ou l'application doit répondre aux événements qui se déclenchent lorsqu'un conflit est détecté. Le fournisseur peut répondre en implémentant les méthodes suivantes :

    Si le fournisseur n'implémente pas ces méthodes, les rappels d'application suivants sont utilisés afin que l'application puisse définir l'action de résolution. Si l'application ne répond pas à ces événements, la résolution du conflit est différée jusqu'à une session de synchronisation suivante. Dans cette situation, le conflit ne sera jamais résolu, sauf si les données en conflit ou l'application changent.

    En réponse au conflit, le fournisseur ou l'application doit définir une action de résolution.

    Outre la définition de l'action de résolution, vous pouvez également inclure du code personnalisé dans le gestionnaire d'événements. Par exemple, vous pouvez afficher les éléments en conflit dans une interface utilisateur à mesure qu'ils sont traités.

  2. Pour certaines actions de résolution définies par Sync Framework ou par l'application, vous devez implémenter l'une des interfaces suivantes ou les deux :

    Pour les conflits d'accès concurrentiel, les méthodes de résolution que vous implémentez pour ces interfaces se distinguent par le type de conflits auxquels elles répondent, par exemple, un conflit de mises à jour. Pour les conflits de contraintes, les méthodes de résolution que vous implémentez se distinguent par le résultat de la résolution, comme l'attribution d'un nouveau nom à l'élément source.

    Pour les conflits d'accès concurrentiel, si l'action est définie sur Merge (pour le code managé) ou sur SRA_MERGE (pour le code non managé), vous devez implémenter les méthodes suivantes qui gèrent les trois types de conflits d'accès concurrentiel :

    L'implémentation doit fusionner les éléments en conflit d'une façon appropriée pour le réplica et pour l'application, sous réserve qu'il existe un élément final représentant les deux éléments en conflit.

    Pour les conflits de contraintes de collision, implémentez des méthodes selon les actions qui peuvent être définies :

Exemple de code managé

Dans cet exemple, les stratégies de gestion des conflits d'accès concurrentiel et des conflits de contraintes sont conservées comme valeurs par défaut d'ApplicationDefined. Cela signifie que l'application s'inscrira aux événements ItemConflicting et ItemConstraint et spécifiera une action visant à résoudre les conflits s'ils se produisent pendant la synchronisation. L'exemple de code suivant illustre les gestionnaires d'événements spécifiés dans le constructeur de MyFullEnumerationSimpleSyncProvider :

this.ItemConstraint += new EventHandler<SimpleSyncItemConstraintEventArgs>(OnItemConstraint);
this.ItemConflicting += new EventHandler<SimpleSyncItemConflictingEventArgs>(OnItemConflicting);
AddHandler Me.ItemConstraint, AddressOf HandleItemConstraint

L'exemple de code suivant illustre les gestionnaires d'événements qui définissent les actions de résolution de conflit sur Merge :

void OnItemConstraint(object sender, SimpleSyncItemConstraintEventArgs e)
{
    // Set the resolution action for constraint conflicts.
    // In this sample, the provider checks for duplicates in InsertItem, and this event would
    // fire if a duplicate occurred. 
    e.SetResolutionAction(ConstraintConflictResolutionAction.Merge);
}

void OnItemConflicting(object sender, SimpleSyncItemConflictingEventArgs e)
{
    // Set the resolution action for concurrency conflicts.
    e.SetResolutionAction(ConflictResolutionAction.Merge);
}
Private Sub HandleItemConstraint(ByVal sender As Object, ByVal e As SimpleSyncItemConstraintEventArgs)
    ' Set the resolution action for constraint conflicts. 
    ' In this sample, the provider checks for duplicates in InsertItem, and this event would 
    ' fire if a duplicate occurred. 
    e.SetResolutionAction(ConstraintConflictResolutionAction.Merge)
End Sub

Private Sub HandleItemConflicting(ByVal sender As Object, ByVal e As SimpleSyncItemConflictingEventArgs)
    ' Set the resolution action for concurrency conflicts. 
    e.SetResolutionAction(ConflictResolutionAction.Merge)
End Sub

L'exemple de code suivant illustre la méthode MergeConstraintConflict implémentée pour répondre à une action de résolution Merge (fusion) pour un conflit de contraintes :

public void MergeConstraintConflict(object itemData, 
    ConflictVersionInformation conflictVersionInformation, 
    IEnumerable<SyncId> changeUnitsToMerge, 
    ItemFieldDictionary localConflictingItem, 
    ItemFieldDictionary keyAndExpectedVersion, 
    RecoverableErrorReportingContext recoverableErrorReportingContext, 
    out ItemFieldDictionary updatedKeyAndVersion)
{
    ItemTransfer transfer = (ItemTransfer)itemData;
    ItemData dataCopy = new ItemData(transfer.ItemData);

    // Combine the conflicting data.
    ItemData mergedData = (_store.Get(transfer.Id)).Merge((ItemData)dataCopy);

    // We are doing a merge so we must delete the old conflicting item from our store.
    ulong idConflicting = (ulong)localConflictingItem[CUSTOM_FIELD_ID].Value;

    _store.DeleteItem(idConflicting);

    // Now create the new merged data in the store.
    if (_store.Contains(transfer.Id))
    {
        _store.UpdateItem(transfer.Id, dataCopy);
    }
    else
    {
        _store.CreateItem(mergedData, transfer.Id);
    }

    updatedKeyAndVersion = _store.CreateItemFieldDictionary(transfer.Id);
}
Public Sub MergeConstraintConflict(ByVal itemData As Object, ByVal conflictVersionInformation As ConflictVersionInformation, ByVal changeUnitsToMerge As IEnumerable(Of SyncId), ByVal localConflictingItem As ItemFieldDictionary, ByVal keyAndExpectedVersion As ItemFieldDictionary, ByVal recoverableErrorReportingContext As RecoverableErrorReportingContext, _
ByRef updatedKeyAndVersion As ItemFieldDictionary) Implements ISimpleSyncProviderConstraintConflictResolver.MergeConstraintConflict
    Dim transfer As ItemTransfer = DirectCast(itemData, ItemTransfer)
    Dim dataCopy As New ItemData(transfer.ItemData)

    ' Combine the conflicting data. 
    Dim mergedData As ItemData = (_store.[Get](transfer.Id)).Merge(DirectCast(dataCopy, ItemData))

    ' We are doing a merge so we must delete the old conflicting item from our store. 
    Dim idConflicting As ULong = CULng(localConflictingItem(CUSTOM_FIELD_ID).Value)

    _store.DeleteItem(idConflicting)

    ' Now create the new merged data in the store. 
    If _store.Contains(transfer.Id) Then
        _store.UpdateItem(transfer.Id, dataCopy)
    Else
        _store.CreateItem(mergedData, transfer.Id)
    End If

    updatedKeyAndVersion = _store.CreateItemFieldDictionary(transfer.Id)
End Sub

Voir aussi

Autres ressources

Implémentation d'un fournisseur personnalisé simple

Procédure : créer un fournisseur simple managé