Partager via


Procédure : filtrer un réplica

Cette rubrique décrit comment utiliser un langage managé pour implémenter un fournisseur Sync Framework qui représente un réplica filtré. Un réplica filtré stocke les données uniquement pour les unités de modification ou les éléments qui sont contenus dans son filtre.

Cette rubrique suppose une connaissance de base des concepts C# et Microsoft .NET Framework.

Les exemples de cette rubrique mettent l'accent sur la classe et le membre Sync Framework suivants :

Présentation des réplicas filtrés

Un réplica filtré stocke uniquement les données d'élément et d'unité de modification pour les éléments et unités de modification qui sont dans son filtre, ainsi que les fantômes, qui sont des métadonnées pour les éléments et unités de modification qui étaient dans le filtre, mais qui ont été exclus. Un réplica filtré effectue aussi le suivi de son filtre et peut également effectuer celui d'autres filtres. Un réplica filtré peut négocier un filtre avec le fournisseur de source, auquel cas le fournisseur de source produit un lot de modifications filtrées. Si le fournisseur de source ne peut pas produire de lot de modifications filtrées, le fournisseur filtré peut filtrer les modifications lui-même et appliquer uniquement celles qui sont dans son filtre.

Un fournisseur filtré implémente IFilteredReplicaNotifyingChangeApplierTarget pour communiquer avec l'applicateur de modifications concernant les éléments qui ont été déplacés par rapport au filtre. Un fournisseur filtré implémente généralement aussi IRequestFilteredSync afin de négocier le filtre que le fournisseur de source utilise pour énumérer les modifications.

Configuration de build

Exemple

L'exemple de code figurant dans cette rubrique montre comment implémenter un fournisseur de destination filtré. Le fournisseur filtré demande un lot de modifications filtrées qui inclut des fantômes, et applique les modifications filtrées et les fantômes au réplica de destination. Dans cet exemple, le réplica stocke des contacts dans un fichier texte de valeurs séparées par des virgules. Les éléments à synchroniser sont les contacts contenus dans ce fichier. Un filtre est une chaîne qui entraîne l'inclusion d'un contact uniquement si la chaîne de filtre est trouvée dans le champ d'adresse du contact.

Négociation du filtre

Lorsque Sync Framework appelle la méthode SpecifyFilter du réplica de destination, ce dernier demande le filtre que le fournisseur de source utilise pour énumérer des modifications. Cet exemple spécifie le premier filtre dans la liste des filtres suivis par le réplica de destination et lève une exception si le fournisseur de source rejette le filtre.

public void SpecifyFilter(FilterRequestCallback filterRequest)
{
    // Use the first tracked filter as the filter for sync.
    if (0 < _ContactStore.TrackedFilters.Count)
    {
        _filterForSync = _ContactStore.TrackedFilters[0];
    }

    // The source provider must agree to send a filtered change batch.
    if (!filterRequest(_filterForSync, FilteringType.CurrentItemsAndVersionsForMovedOutItems))
    {
        throw new SyncInvalidOperationException("Filter specified by SpecifyFilter was rejected.");
    }
}

Application des modifications filtrées

Le fournisseur de destination utilise un applicateur de modifications pour traiter le lot de modifications. Le service de stockage des métadonnées ne prenant pas en charge le filtrage personnalisé, la liste des versions locales doit être mise à jour pour marquer les modifications de fantômes avant que la liste ne soit envoyée à l'applicateur de modifications.

public override void ProcessChangeBatch(ConflictResolutionPolicy resolutionPolicy, ChangeBatch sourceChanges, object changeDataRetriever, SyncCallbacks syncCallbacks, SyncSessionStatistics sessionStatistics)
{
    // Use the metadata storage service to get the local versions of changes received from the source provider.
    IEnumerable<ItemChange> localVersions = _ContactStore.ContactReplicaMetadata.GetLocalVersions(sourceChanges);
    // Copy and fix up the local version list to include ghost information.
    List<ItemChange> fixedLocalVersions = new List<ItemChange>();
    ChangeKind fixedChangeKind;
    foreach (ItemChange localVersion in localVersions)
    {
        fixedChangeKind = localVersion.ChangeKind;
        if (localVersion.ChangeKind != ChangeKind.UnknownItem && _ContactStore.IsGhost(localVersion.ItemId))
        {
            fixedChangeKind = ChangeKind.Ghost;
        }
        fixedLocalVersions.Add(new ItemChange(IdFormats, localVersion.ReplicaId, localVersion.ItemId, fixedChangeKind, localVersion.CreationVersion,
            localVersion.ChangeVersion));
    }

    // Use a NotifyingChangeApplier object to process the changes. Note that the provider object is passed as the INotifyingChangeApplierTarget
    // object that will be called to apply changes to the item store.
    NotifyingChangeApplier changeApplier = new NotifyingChangeApplier(ContactStore.ContactIdFormatGroup);
    changeApplier.ApplyChanges(resolutionPolicy, sourceChanges, (IChangeDataRetriever)changeDataRetriever,
        fixedLocalVersions, _ContactStore.ContactReplicaMetadata.GetKnowledge(), 
        _ContactStore.ContactReplicaMetadata.GetForgottenKnowledge(), this, _sessionContext, syncCallbacks);
}

L'applicateur de modifications appelle la méthode SaveItemChange pour enregistrer les modifications. Un réplica filtré gère les actions de modification qui affectent les éléments fantômes.

case SaveChangeAction.CreateGhost:
case SaveChangeAction.UpdateGhost:
{
    try
    {
        _ContactStore.UpdateGhostFromSync(change, _filterKeyMap);
    }
    catch (Exception ex)
    {
        RecoverableErrorData errData = new RecoverableErrorData(ex);
        context.RecordRecoverableErrorForItem(errData);
    }

    break;
}

case SaveChangeAction.MarkItemAsGhost:
{
    try
    {
        // Delete the item from the contact store and update the metadata to indicate it is a ghost.
        _ContactStore.MarkItemAsGhost(change, _filterKeyMap);
    }
    catch (Exception ex)
    {
        RecoverableErrorData errData = new RecoverableErrorData(ex);
        context.RecordRecoverableErrorForItem(errData);
    }

    break;
}

case SaveChangeAction.UnmarkItemAsGhost:
{
    try
    {
        // Create the item in the contact store and update the metadata to indicate the item is not a ghost.
        _ContactStore.UnmarkItemAsGhost(change, (string)context.ChangeData, _filterKeyMap);
    }
    catch (Exception ex)
    {
        RecoverableErrorData errData = new RecoverableErrorData(ex);
        context.RecordRecoverableErrorForItem(errData);
    }

    break;
}

case SaveChangeAction.DeleteGhostAndStoreTombstone:
{
    try
    {
        _ContactStore.DeleteGhostFromSync(change.ItemId, change.ChangeVersion);
    }
    catch (Exception ex)
    {
        RecoverableErrorData errData = new RecoverableErrorData(ex);
        context.RecordRecoverableErrorForItem(errData);
    }

    break;
}
public void UpdateGhostFromSync(ItemChange itemChange, FilterKeyMap providerFilterKeyMap)
{
    // Find the ghost metadata in our list or load it from the metadata store.
    ItemMetadata itemMeta = null;
    if (_ContactGhostMetaList.ContainsKey(itemChange.ItemId))
    {
        itemMeta = _ContactGhostMetaList[itemChange.ItemId];
    }
    else
    {
        itemMeta = _ContactReplicaMetadata.FindItemMetadataById(itemChange.ItemId);
    }

    // The ghost does not exist, so create it and add it to the metadata store.
    if (null == itemMeta)
    {
        itemMeta = _ContactReplicaMetadata.CreateItemMetadata(itemChange.ItemId,
            itemChange.CreationVersion);

        InitializeFilterTrackingFields(itemMeta);

        // Create values for all index fields in the metadata store.
        itemMeta.SetCustomField(FirstNameField, itemChange.ItemId.ToString());
        itemMeta.SetCustomField(LastNameField, "0");
        itemMeta.SetCustomField(PhoneNumberField, "0");

        _ContactGhostMetaList.Add(itemMeta.GlobalId, itemMeta);
    }

    // Set the version metadata for the change unit by using the metadata storage service.
    itemMeta.ChangeVersion = itemChange.ChangeVersion;

    // Update the filter tracking metadata for filter change metadata sent from the source provider.
    for (int iFilter = 0; iFilter < _trackedFilters.Count; iFilter++)
    {
        // Get filter change metadata from the source provider for this change, if it exists.
        FilterChange filterChange = GetFilterChange(itemChange, iFilter, providerFilterKeyMap);

        // If filter change metadata is present, use it to update the item metadata.
        if (null != filterChange)
        {
            SetIsInFilter(itemMeta, iFilter, filterChange.IsMoveIn);
            SetMoveVersion(itemMeta, iFilter, filterChange.MoveVersion);
        }
    }
}

public bool IsGhost(SyncId itemId)
{
    bool isGhost = false;

    ItemMetadata itemMeta = _ContactReplicaMetadata.FindItemMetadataById(itemId);
    if (null != itemMeta)
    {
        // The item is a ghost if it is not deleted and it does not exist in the contact store.
        isGhost = (!itemMeta.IsDeleted && !_ContactList.ContainsKey(itemId));
    }

    return isGhost;
}

public void MarkItemAsGhost(ItemChange itemChange, FilterKeyMap providerFilterKeyMap)
{
    // Delete the item from the contact store.
    _ContactList.Remove(itemChange.ItemId);

    // Move the item from the active metadata list to the ghost list.
    ItemMetadata ghostMeta = _ContactItemMetaList[itemChange.ItemId];
    _ContactGhostMetaList.Add(itemChange.ItemId, ghostMeta);
    _ContactItemMetaList.Remove(itemChange.ItemId);

    // Update the filter tracking metadata for filter change metadata sent from the source provider.
    for (int iFilter = 0; iFilter < _trackedFilters.Count; iFilter++)
    {
        // Get filter change metadata from the source provider for this change, if it exists.
        FilterChange filterChange = GetFilterChange(itemChange, iFilter, providerFilterKeyMap);

        // If filter change metadata is present, use it to update the item metadata.
        if (null != filterChange)
        {
            SetIsInFilter(ghostMeta, iFilter, filterChange.IsMoveIn);
            SetMoveVersion(ghostMeta, iFilter, filterChange.MoveVersion);
        }
    }
}

public void UnmarkItemAsGhost(ItemChange itemChange, string changeData, FilterKeyMap providerFilterKeyMap)
{
    // Get the metadata for the ghost.
    ItemMetadata itemMeta = null;
    if (_ContactGhostMetaList.ContainsKey(itemChange.ItemId))
    {
        itemMeta = _ContactGhostMetaList[itemChange.ItemId];
    }
    else
    {
        itemMeta = _ContactReplicaMetadata.FindItemMetadataById(itemChange.ItemId);
    }

    if (null == itemMeta)
    {
        throw new SyncInvalidOperationException("UnmarkItemAsGhost received an item but has not metadata for the item.");
    }

    // Create a new contact and add it to the contact store.
    Contact contact = new Contact();
    _ContactList.Add(itemMeta.GlobalId, contact);
    _ContactList[itemChange.ItemId].FromString(changeData);

    // Move the metadata from the ghost list to the active list.
    _ContactItemMetaList.Add(itemMeta.GlobalId, itemMeta);
    _ContactGhostMetaList.Remove(itemMeta.GlobalId);

    // Update the metadata for the item.
    UpdateContactMetadataInternal(itemChange.ItemId, itemChange.ChangeVersion, itemChange, providerFilterKeyMap);
}

// Mark a ghost as deleted in the metadata store.
public void DeleteGhostFromSync(SyncId itemId, SyncVersion changeVersion)
{
    // Find the item in the ghost metadata list or load it from the metadata store.
    ItemMetadata itemMeta = null;
    if (_ContactGhostMetaList.ContainsKey(itemId))
    {
        itemMeta = _ContactGhostMetaList[itemId];
    }
    else
    {
        itemMeta = _ContactReplicaMetadata.FindItemMetadataById(itemId);
    }

    if (null == itemMeta)
    {
        throw new SyncInvalidOperationException("DeleteGhostFromSync received item but has no metadata.");
    }

    //Mark item as deleted.
    itemMeta.MarkAsDeleted(changeVersion);

    // Clear the index name field so it doesn't collide with future items.
    itemMeta.SetCustomField(FirstNameField, itemMeta.GlobalId.ToString());

    // Move the item to the deleted list.
    _ContactDeletedItemMetaList.Add(itemMeta);
    _ContactGhostMetaList.Remove(itemMeta.GlobalId);
}

Énumération des éléments inclus dans le filtre

Le fournisseur filtré implémente IFilteredReplicaNotifyingChangeApplierTarget pour communiquer avec l'applicateur de modifications concernant les éléments qui ont été déplacés par rapport au filtre. Cet exemple énumère tous les éléments du magasin des métadonnées et ajoute un élément à la liste retournée lorsque l'élément est dans le filtre pour le réplica, et que la version de déplacement de l'élément n'est pas contenue dans la connaissance de base spécifiée.

       public IEnumerator<SyncId>  GetNewMoveInItems(SyncKnowledge baseKnowledge)
       {
            List<SyncId> newMoveInIdList = new List<SyncId>();
           IEnumerable<ItemMetadata> allItems = _ContactStore.ContactReplicaMetadata.GetAllItems(false);
           SyncKnowledge mappedBaseKnowledge = _ContactStore.ContactReplicaMetadata.GetKnowledge().MapRemoteKnowledgeToLocal(baseKnowledge);
           foreach (ItemMetadata itemMeta in allItems)
           {
               FilterChange filterChange = _ContactStore.GetTrackedFilterMetadata(itemMeta, _filterForSync);
               if (filterChange.IsMoveIn)
               {
                   if (!mappedBaseKnowledge.Contains(_ContactStore.ContactReplicaMetadata.ReplicaId, itemMeta.GlobalId, filterChange.MoveVersion))
                   {
                       newMoveInIdList.Add(itemMeta.GlobalId);
                   }
               }
           }
           return newMoveInIdList.GetEnumerator();
       }

Étapes suivantes

Vous pouvez ensuite ajouter la négociation de filtres à votre fournisseur afin qu'il puisse communiquer avec le fournisseur de destination pour décider du filtre à utiliser pour l'énumération des modifications. Pour plus d'informations sur la négociation de filtres, consultez Procédure : négocier un filtre.

Vous souhaiterez peut-être aussi permettre à votre fournisseur de suivre les filtres. Les réplicas de suivi de filtre maintiennent une taille réduite de connaissance lorsqu'ils envoient des modifications aux réplicas filtrés. Pour plus d'informations sur la façon d'implémenter un fournisseur de suivi des filtres, consultez Procédure : suivre des filtres et énumérer des modifications filtrées.

Voir aussi

Autres ressources

Programmation de tâches de fournisseurs personnalisés standard courantes

Filtrage des données de synchronisation