Partager via


Procédure : suivre des filtres et énumérer des modifications filtrées

Cette rubrique décrit comment utiliser un langage managé pour suivre les filtres qui sont utilisés dans une communauté de synchronisation Sync Framework, et comment énumérer un lot de modifications filtrées.

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

Les exemples de cette rubrique reposent sur les classes et les membres Sync Framework suivants :

Présentation du suivi de filtre

Il est recommandé que tous les réplicas dans une communauté de synchronisation effectuent le suivi des filtres utilisés dans la communauté. Lorsqu'un réplica filtré reçoit une énumération des modifications filtrée d'un réplica de suivi de filtre, la taille de la connaissance du réplica filtré est minimisée. Lorsqu'un réplica filtré reçoit une énumération des modifications filtrée d'un réplica qui ne suit pas le filtre, la taille de la connaissance augmente par rapport au nombre de modifications envoyé.

Un fournisseur de suivi de filtre implémente l'interface IFilterTrackingProvider. Sync Framework utilise cette interface pour arbitrer la négociation des filtres qui sont suivis à la fois par le réplica source et le réplica de destination.

Le fournisseur de source envoie des métadonnées de filtre pour chaque filtre mutuellement suivi pendant la synchronisation. Un fournisseur de suivi des filtres implémente aussi généralement l'interface ISupportFilteredSync et est en mesure d'énumérer les modifications qui sont filtrées par un filtre suivi quelconque.

Ce fournisseur de destination implémente l'interface IFilterTrackingNotifyingChangeApplierTarget, que l'applicateur de modifications utilise pour obtenir et mettre à jour le mappage de clés de filtre et la connaissance oubliée du filtre des filtres suivis. Le fournisseur de destination met aussi à jour les métadonnées de modification de filtre des unités de modification et des éléments qui sont envoyés à la méthode SaveItemChange ou SaveChangeWithChangeUnits.

Configuration de build

Exemple

L'exemple de code dans cette rubrique illustre comment implémenter un fournisseur de suivi de filtre qui envoie des métadonnées de modification de filtre et qui peut énumérer un lot de modifications filtrées lorsqu'il agit en tant que fournisseur de source, et qui applique les métadonnées de modification de filtre lorsqu'il agit en tant que fournisseur 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 des filtres faisant l'objet d'un suivi

Pour négocier les filtres suivis, Sync Framework appelle SpecifyTrackedFilters sur le fournisseur de destination. Cet exemple énumère la liste des filtres suivis par le réplica de destination, envoie chaque filtre au fournisseur de source, puis ajoute le filtre à la liste des filtres mutuellement suivis seulement si le fournisseur de source suit également le filtre.

public void SpecifyTrackedFilters(RequestTrackedFilterCallback filterTrackingRequestCallback)
{
    foreach (AddressFilter filter in _ContactStore.TrackedFilters)
    {
        if (filterTrackingRequestCallback(filter))
        {
            // Add the filter to the list of mutually tracked filters only when the 
            // source provider also tracks the filter.
            _filterKeyMap.AddFilter(filter);
        }
    }
}

Pour chaque filtre qui est énuméré par le fournisseur de destination, Sync Framework appelle la méthode TryAddTrackedFilter du fournisseur de source. Cet exemple vérifie si le filtre spécifié est suivi par le réplica source et, le cas échéant, l'ajoute à la liste des filtres mutuellement suivis.

public bool TryAddTrackedFilter(ISyncFilter filter)
{
    bool isTracked = false;
    foreach (AddressFilter addressFilter in _ContactStore.TrackedFilters)
    {
        // Add the filter to the list of mutually tracked filters only when it
        // is identical to one of the filters of this replica.
        if (addressFilter.IsIdentical(filter))
        {
            _filterKeyMap.AddFilter(addressFilter);
            isTracked = true;
            break;
        }
    }
    return isTracked;
}

Négociation du filtre utilisé pour la synchronisation

Lorsque le réplica de destination est un réplica filtré, il demande généralement le filtre que le fournisseur de source utilise pour énumérer des modifications. Le fournisseur de source reçoit cette demande via la méthode TryAddFilter(Object). Cet exemple vérifie si le filtre demandé est mutuellement suivi et, le cas échéant, l'enregistre afin de l'utiliser pour énumérer des modifications.

public bool TryAddFilter(object filter, FilteringType filteringType)
{
    _filterForSync = null;

    // The filter must be tracked by both replicas.
    for (int filterKey = 0; filterKey < _filterKeyMap.Count; filterKey++)
    {
        if (_filterKeyMap[filterKey].IsIdentical((ISyncFilter)filter))
        {
            _filterForSync = (AddressFilter)_filterKeyMap[filterKey];
            _filteringType = filteringType;
            break;
        }
    }

    return (null != _filterForSync);
}

Envoi de métadonnées de filtre et de modifications filtrées

Le fournisseur de source envoie des métadonnées de filtre lorsqu'il envoie un lot de modifications. Cet exemple peut envoyer un lot de modifications filtrées ou non, selon si un filtre est utilisé ou non pour la synchronisation. Chaque modification qui est incluse dans le lot de modifications est associée à des métadonnées de modifications de filtre lorsque l'élément a été modifié en fonction d'un filtre suivi. La connaissance oubliée du filtre pour chaque filtre suivi est aussi envoyée. Lorsqu'un filtre est utilisé pour la synchronisation, un élément est envoyé lorsqu'il est dans le filtre, et un élément qui a été exclu du filtre est marqué comme fantôme.

public override ChangeBatch GetChangeBatch(uint batchSize, SyncKnowledge destinationKnowledge, out object changeDataRetriever)
{
    // Return this object as the IChangeDataRetriever object that is called to retrieve item data.
    changeDataRetriever = this;

    // The metadata storage service does not support filter tracking, so enumerate changes manually.
    ChangeBatch changeBatch;
    if (null == _filterForSync)
    {
        // No filter was specified for synchronization, so produce an unfiltered change batch.
        changeBatch = new ChangeBatch(IdFormats, destinationKnowledge, _ContactStore.ContactReplicaMetadata.GetForgottenKnowledge());
    }
    else
    {
        // A filter was specified for synchronization, so produce a filtered change batch.
        CustomFilterInfo filterInfo = new CustomFilterInfo(IdFormats, _filterForSync);
        changeBatch = new ChangeBatch(IdFormats, destinationKnowledge, _filterForSync.FilterForgottenKnowledge, filterInfo);
    }

    // If the destination replica tracks filters that are tracked by the source replica, 
    // set the filter key map of the change batch.
    if (0 < FilterKeyMap.Count)
    {
        // Add the filter key map to the change batch before any groups are started.
        changeBatch.FilterKeyMap = FilterKeyMap;
    }

    // Get all the items from the metadata store.
    IEnumerable<ItemMetadata> allItems = _ContactStore.ContactReplicaMetadata.GetAllItems(true);

    // Convert the destination knowledge for use with local versions.
    SyncKnowledge mappedDestKnowledge = _ContactStore.ContactReplicaMetadata.GetKnowledge().MapRemoteKnowledgeToLocal(destinationKnowledge);

    // Build the list of items in the change batch.
    List<ItemChange> itemChanges = new List<ItemChange>((int)batchSize);
    uint cItemsInBatch = 0;
    SyncId replicaId = _ContactStore.ContactReplicaMetadata.ReplicaId;
    foreach (ItemMetadata itemMeta in allItems)
    {
        // Process all items if this is an unfiltered enumeration.
        // Otherwise, only process an item that has been in the filter. An item has been in the filter if
        // it is currently in the filter or if its move version in relation to the filter is a value
        // other than (0,0).
        if (null == _filterForSync || _ContactStore.HasBeenInFilter(itemMeta, _filterForSync))
        {
            // If a change is not contained in the destination knowledge, add it to the change batch.
            if (!mappedDestKnowledge.Contains(replicaId, itemMeta.GlobalId, itemMeta.ChangeVersion))
            {
                ChangeKind kind;
                if (itemMeta.IsDeleted)
                {
                    kind = ChangeKind.Deleted;
                }
                // An item that has been in the filter but is not currently in the filter has
                // recently moved out, so it must be marked as a ghost.
                else if (null != _filterForSync 
                    && !_filterForSync.IsInFilter(_ContactStore.ContactList[itemMeta.GlobalId]))
                {
                    kind = ChangeKind.Ghost;
                }
                else
                {
                    kind = ChangeKind.Update;
                }

                ItemChange itemChange = new ItemChange(IdFormats, _ContactStore.ContactReplicaMetadata.ReplicaId,
                    itemMeta.GlobalId, kind, itemMeta.CreationVersion, itemMeta.ChangeVersion);

                // Pass along any filter information for filters tracked by both the source and destination replicas.
                _ContactStore.AddFilterChanges(_filterKeyMap, itemMeta, mappedDestKnowledge, itemChange);

                // Add the item to the change list. Include ghosts only if the destination requested ghosts.
                if (kind != ChangeKind.Ghost || (kind == ChangeKind.Ghost && FilteringType.CurrentItemsAndVersionsForMovedOutItems == _filteringType))
                {
                    itemChanges.Add(itemChange);
                }
                cItemsInBatch++;
            }
        }

        if (batchSize <= cItemsInBatch)
        {
            break;
        }
    }

    // Add the list of items to the change batch object.
    if (0 < itemChanges.Count)
    {
        changeBatch.BeginOrderedGroup(itemChanges[0].ItemId);

        // Set the filter forgotten knowledge for each filter that the destination has requested.
        for (int iFilter = 0; iFilter < FilterKeyMap.Count; iFilter++)
        {
            AddressFilter addressFilter = (AddressFilter)FilterKeyMap[iFilter];
            changeBatch.SetFilterForgottenKnowledge((uint)iFilter, addressFilter.FilterForgottenKnowledge);
        }

        changeBatch.AddChanges(itemChanges);

        // End the group of changes in the change batch. Pass the current source knowledge.
        changeBatch.EndOrderedGroup(itemChanges[itemChanges.Count - 1].ItemId, _ContactStore.ContactReplicaMetadata.GetKnowledge());

        // If all items were enumerated before the batch was filled, then this is the last batch.
        if (cItemsInBatch < batchSize)
        {
            changeBatch.SetLastBatch();
        }
    }
    else 
    {
        throw new InvalidOperationException("GetChangeBatch called but there are no new changes to enumerate.");
    }

    return changeBatch;
}

Les métadonnées de modification de filtre sont examinées pour chaque filtre mutuellement suivi. Les métadonnées de modification du filtre sont ajoutées à une modification quand la version de déplacement d'une modification n'est pas contenue dans la connaissance de destination.

public void AddFilterChanges(FilterKeyMap filterKeyMap, ItemMetadata itemMeta, SyncKnowledge destKnowledge,
    ItemChange itemChange)
{
    for (int filterKey = 0; filterKey < filterKeyMap.Count; filterKey++)
    {
        // Find the filter in the list of all filters tracked by this replica.
        int iFilter = 0;
        for (; iFilter < _trackedFilters.Count; iFilter++)
        {
            if (filterKeyMap[filterKey].IsIdentical(_trackedFilters[iFilter]))
            {
                break;
            }
        }

        // Get the filter information for the item and add it to the ItemChange object.
        SyncVersion moveVersion = GetMoveVersion(itemMeta, iFilter);

        // Only return a filter change if the destination knowledge does not contain the version of the 
        // last move that occurred in relation to the specified filter.
        FilterChange filterChange = null;
        if (!destKnowledge.Contains(ContactReplicaMetadata.ReplicaId, itemMeta.GlobalId, moveVersion))
        {
            filterChange = new FilterChange(GetIsInFilter(itemMeta, iFilter), moveVersion);
            itemChange.AddFilterChange((uint)filterKey, filterChange);
        }
    }
}

Application des métadonnées de filtre

Les modifications sont appliquées à l'aide d'un applicateur de modifications, qui appelle la méthode SaveItemChange du fournisseur de destination. Lorsqu'un élément est créé ou mis à jour, cet exemple crée ou met à jour les données dans le magasin de contacts, met à jour les métadonnées de synchronisation, puis met à jour les métadonnées de suivi de filtre. Les métadonnées de suivi de filtre sont mises à jour pour tous les filtres qui sont suivis par le fournisseur de destination, et pas seulement pour les filtres mutuellement suivis. Lorsque les métadonnées de filtre sont envoyées par le fournisseur de source, elles sont utilisées , autrement la modification est comparée à chaque filtre suivi et les métadonnées de modification de filtre appropriées sont définies.

public void UpdateContactFromSync(ItemChange itemChange, string changeData, FilterKeyMap providerFilterKeyMap)
{
    if (!_ContactList.ContainsKey(itemChange.ItemId))
    {
        // The item does not exist, so create a new contact and add it to the contact and metadata store.
        Contact contact = new Contact();
        ItemMetadata itemMeta = _ContactReplicaMetadata.CreateItemMetadata(itemChange.ItemId,
            itemChange.CreationVersion);

        InitializeFilterTrackingFields(itemMeta);

        _ContactList.Add(itemMeta.GlobalId, contact);
        _ContactItemMetaList.Add(itemMeta.GlobalId, itemMeta);
    }

    _ContactList[itemChange.ItemId].FromString(changeData);

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

private void UpdateContactMetadataInternal(SyncId itemId, SyncVersion version, ItemChange itemChange, FilterKeyMap providerFilterKeyMap)
{
    ItemMetadata itemMeta = _ContactItemMetaList[itemId];

    // Set the value of all index fields in the metadata store.
    itemMeta.SetCustomField(FirstNameField, _ContactList[itemId].FirstName);
    itemMeta.SetCustomField(LastNameField, _ContactList[itemId].LastName);
    itemMeta.SetCustomField(PhoneNumberField, _ContactList[itemId].PhoneNumber);

    // Update the version metadata for the change unit.
    itemMeta.ChangeVersion = version;

    // Update the filter tracking metadata both for filter change metadata sent from the source provider and for
    // any other filters tracked by this replica.
    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);
        }
        // Otherwise, update the item metadata for other tracked filters.
        else
        {
            UpdateFilterTrackingMetadata(itemMeta, iFilter, version);
        }
    }
}

// An item has been created or has changed, so update the filter tracking metadata for the item.
void UpdateFilterTrackingMetadata(ItemMetadata itemMeta, int iFilter, SyncVersion moveVersion)
{
    // Determine whether the item is in the filter.
    Contact contact = _ContactList[itemMeta.GlobalId];
    bool isInFilter = _trackedFilters[iFilter].IsInFilter(contact);

    // Determine whether the item was in the filter.
    bool wasInFilter = GetIsInFilter(itemMeta, iFilter);

    // If the filter membership has changed, update the filter tracking metadata.
    if (isInFilter != wasInFilter)
    {
        SetIsInFilter(itemMeta, iFilter, isInFilter);
        SetMoveVersion(itemMeta, iFilter, moveVersion);
    }
}

L'applicateur de modifications appelle aussi les méthodes de l'interface IFilterTrackingNotifyingChangeApplierTarget du fournisseur de destination afin d'obtenir et d'enregistrer des métadonnées de suivi de filtre. Cet exemple retourne les objets demandés et enregistre les métadonnées spécifiées.

private FilterKeyMap _filterKeyMap;

public FilterKeyMap FilterKeyMap
{
    get 
    {
        return _filterKeyMap;
    }
}

public ForgottenKnowledge GetFilterForgottenKnowledge(uint filterIndex)
{
    if (filterIndex < _filterKeyMap.Count)
    {
        return ((AddressFilter)_filterKeyMap[(int)filterIndex]).FilterForgottenKnowledge;
    }
    else
    {
        throw new ArgumentOutOfRangeException("GetFilterForgottenKnowledge received and out-of-range index.");
    }
}

public void SaveKnowledgeWithFilterForgottenKnowledge(SyncKnowledge syncKnowledge, ForgottenKnowledge forgottenKnowledge, ForgottenKnowledge[] filterForgottenKnowledge)
{
    // First update the list of filter forgotten knowledge objects.
    for (int iFilter = 0; iFilter < filterForgottenKnowledge.Length; iFilter++)
    {
        ((AddressFilter)_filterKeyMap[iFilter]).FilterForgottenKnowledge = filterForgottenKnowledge[iFilter];
    }

    // Update the list of filters that are stored in the custom replica metadata.
    AddressFilter.StoreFiltersInReplicaMetadata(_ContactStore.ContactReplicaMetadata, _ContactStore.TrackedFilters);

    // Store the remaining knowledge objects.
    StoreKnowledgeForScope(syncKnowledge, forgottenKnowledge);
}

Stockage des métadonnées de filtre

Les filtres qui sont suivis par un réplica doivent être stockés dans le réplica, avec la connaissance oubliée de filtre de chaque filtre suivi. Cet exemple utilise le service de stockage des métadonnées pour stocker les métadonnées. Le service de stockage des métadonnées ne prend pas en charge les filtres personnalisés ; les filtres suivis sont sérialisés en flux d'octets et stockés dans le champ de métadonnées de réplica personnalisé du magasin de métadonnées.

class AddressFilter : ISyncFilter, ISyncFilterDeserializer
{
    // For deserialization.
    public AddressFilter()
    {
        _filter = null;        
    }

    // A filter is a string that is compared against the Address field of a contact.
    public AddressFilter(string filter)
    {
        _filter = filter;
    }

    public string Filter
    {
        get
        {
            return _filter;
        }
    }

    // A contact is in the filter when the filter string is contained in the Address field of the contact.
    public bool IsInFilter(Contact contact)
    {
        return contact.Address.Contains(_filter);
    }

    private string _filter;

    public ForgottenKnowledge FilterForgottenKnowledge
    {
        get 
        {
            return _filterForgottenKnowledge;
        }

        set
        {
            _filterForgottenKnowledge = value;
        }
    }

    private ForgottenKnowledge _filterForgottenKnowledge;

    #region ISyncFilter Members

    // Two filters are identical when their filter strings are equal.
    public bool IsIdentical(ISyncFilter otherFilter)
    {
        return _filter.Equals(((AddressFilter)otherFilter).Filter);
    }

    public byte[] Serialize()
    {
        MemoryStream memStream = new MemoryStream();
        BinaryWriter biWriter = new BinaryWriter(memStream, Encoding.Unicode);

        SerializeToBinaryWriter(biWriter);

        return memStream.GetBuffer();
    }

    private void SerializeToBinaryWriter(BinaryWriter biWriter)
    {
        bool hasFilterForgottenKnowledge = (null != _filterForgottenKnowledge);

        biWriter.Write(hasFilterForgottenKnowledge);

        biWriter.Write(_filter);

        if (null != _filterForgottenKnowledge)
        {
            byte[] serializedForgottenKnowledge = _filterForgottenKnowledge.Serialize();
            biWriter.Write(serializedForgottenKnowledge.Length);
            biWriter.Write(serializedForgottenKnowledge);
        }
    }

    #endregion

    #region ISyncFilterDeserializer Members

    public ISyncFilter Deserialize(byte[] data)
    {
        MemoryStream memStream = new MemoryStream(data, 0, data.Length, false, true);
        BinaryReader biReader = new BinaryReader(memStream, Encoding.Unicode);

        DeserializeFromBinaryReader(biReader, memStream);

        return this;
    }

    private void DeserializeFromBinaryReader(BinaryReader biReader, MemoryStream memStream)
    {
        bool hasFilterForgottenKnowledge = biReader.ReadBoolean();

        _filter = biReader.ReadString();

        if (hasFilterForgottenKnowledge)
        {
            int cbForgottenKnowledge = biReader.ReadInt32();
            byte[] rawBuffer = biReader.ReadBytes(cbForgottenKnowledge);
            _filterForgottenKnowledge = ForgottenKnowledge.Deserialize(ContactStore.ContactIdFormatGroup,
                rawBuffer);
        }
    }

    #endregion

    // This implementation uses the metadata storage service to store metadata.
    // The metadata storage service does not support custom filters, so store the filters 
    // that are tracked by a replica in the custom replica metadata field
    // of the metadata store.
    public static void StoreFiltersInReplicaMetadata(ReplicaMetadata repMeta, List<AddressFilter> filters)
    {
        MemoryStream memStream = new MemoryStream();
        BinaryWriter biWriter = new BinaryWriter(memStream, Encoding.Unicode);

        biWriter.Write(filters.Count);

        foreach (AddressFilter filter in filters)
        {
            filter.SerializeToBinaryWriter(biWriter);
        }

        repMeta.CustomReplicaMetadata = memStream.GetBuffer();
    }

    public static List<AddressFilter> ReadFiltersFromReplicaMetadata(ReplicaMetadata repMeta)
    {
        MemoryStream memStream = new MemoryStream(repMeta.CustomReplicaMetadata, 0, repMeta.CustomReplicaMetadata.Length, false, true);
        BinaryReader biReader = new BinaryReader(memStream, Encoding.Unicode);

        int cFilters = biReader.ReadInt32();
        List<AddressFilter> filters = new List<AddressFilter>(cFilters);
        AddressFilter newFilter;
        for (int iFilter = 0; iFilter < cFilters; iFilter++)
        {
            newFilter = new AddressFilter();
            newFilter.DeserializeFromBinaryReader(biReader, memStream);
            filters.Add(newFilter);
        }

        return filters;
    }

    public override string ToString()
    {
        return _filter;
    }
}

Chaque élément suit s'il est contenu dans un filtre, et la version de la modification qui a entraîné l'inclusion dans le filtre ou son exclusion. Cet exemple stocke les métadonnées de suivi de filtre pour un élément en tant que champs d'éléments personnalisés dans le magasin des métadonnées.

// Allocate space for the filter tracking metadata for each tracked filter.
private void InitializeFilterTrackingFields(ItemMetadata itemMeta)
{
    if (0 < _trackedFilters.Count)
    {
        byte[] newIsInFilterBytes = new byte[_trackedFilters.Count];
        byte[] newMoveVersionBytes = new byte[_trackedFilters.Count * (sizeof(uint) + sizeof(ulong))];
        itemMeta.SetCustomField(IsInFiltersField, newIsInFilterBytes);
        itemMeta.SetCustomField(MoveVersionsField, newMoveVersionBytes);
    }
}

// Gets a value that indicates whether the specified item is in the specified filter,
// according to the filter tracking metadata.
private bool GetIsInFilter(ItemMetadata itemMeta, int iFilter)
{
    byte[] isInFilterList = itemMeta.GetBytesField(IsInFiltersField);
    return (1 == isInFilterList[iFilter]);
}

// Sets a value that indicates whether the specified item is in the specified filter.
private void SetIsInFilter(ItemMetadata itemMeta, int iFilter, bool isInFilter)
{
    byte[] isInFilterList = itemMeta.GetBytesField(IsInFiltersField);
    isInFilterList[iFilter] = (byte)(isInFilter ? 1 : 0);
    itemMeta.SetCustomField(IsInFiltersField, isInFilterList);
}

// Gets the version of the change that caused the specified item to move in relation
// to the specified filter.
private SyncVersion GetMoveVersion(ItemMetadata itemMeta, int iFilter)
{
    // Get the raw bytes for the move version list.
    byte[] moveVersionBytes = itemMeta.GetBytesField(MoveVersionsField);

    // Read the SyncVersion elements from the specified location in the byte array.
    MemoryStream memStream = new MemoryStream(moveVersionBytes);
    memStream.Seek(iFilter * (sizeof(uint) + sizeof(ulong)), SeekOrigin.Begin);
    BinaryReader biReader = new BinaryReader(memStream, Encoding.Unicode);
    uint replicaKey = biReader.ReadUInt32();
    ulong tickCount = biReader.ReadUInt64();

    SyncVersion moveVersion = new SyncVersion(replicaKey, tickCount);

    return moveVersion;
}

// Sets the version of the change that caused the specified item to move in relation
// to the specified filter.
private void SetMoveVersion(ItemMetadata itemMeta, int iFilter, SyncVersion moveVersion)
{
    // Get the raw bytes for the move version list.
    byte[] moveVersionBytes = itemMeta.GetBytesField(MoveVersionsField);

    // Write the SyncVersion elements to the specified location in the byte array.
    MemoryStream memStream = new MemoryStream(moveVersionBytes);
    memStream.Seek(iFilter * (sizeof(uint) + sizeof(ulong)), SeekOrigin.Begin);
    BinaryWriter biWriter = new BinaryWriter(memStream, Encoding.Unicode);
    biWriter.Write(moveVersion.ReplicaKey);
    biWriter.Write(moveVersion.TickCount);

    itemMeta.SetCustomField(MoveVersionsField, moveVersionBytes);
}

// Set up fields used to track a new filter.
public bool StartTrackingFilter(AddressFilter filter)
{
    bool filterIsNew = true;

    foreach (AddressFilter addressFilter in _trackedFilters)
    {
        if (addressFilter.IsIdentical(filter))
        {
            filterIsNew = false;
            break;
        }
    }

    if (filterIsNew)
    {
        // Initialize the filter forgotten knowledge to the current knowledge of the replica.
        filter.FilterForgottenKnowledge = new ForgottenKnowledge(ContactStore.ContactIdFormatGroup,
            ContactReplicaMetadata.GetKnowledge());
        _trackedFilters.Add(filter);

        // Allocate new space for and initialize filter tracking metadata for all active items.
        byte[] newIsInFilterBytes = new byte[_trackedFilters.Count];
        byte[] newMoveVersionBytes = new byte[_trackedFilters.Count * (sizeof(uint) + sizeof(ulong))];
        int iFilter = _trackedFilters.Count - 1;
        foreach (ItemMetadata itemMeta in _ContactItemMetaList.Values)
        {
            // Get current filter tracking metadata, copy it to the new byte arrays, and store it.
            byte[] isInFilterBytes = itemMeta.GetBytesField(IsInFiltersField);
            byte[] moveVersionBytes = itemMeta.GetBytesField(MoveVersionsField);

            if (null != isInFilterBytes)
            {
                isInFilterBytes.CopyTo(newIsInFilterBytes, 0);
            }
            if (null != moveVersionBytes)
            {
                moveVersionBytes.CopyTo(newMoveVersionBytes, 0);
            }

            itemMeta.SetCustomField(IsInFiltersField, newIsInFilterBytes);
            itemMeta.SetCustomField(MoveVersionsField, newMoveVersionBytes);

            // Initialize filter tracking metadata.
            bool isInFilter = filter.IsInFilter(_ContactList[itemMeta.GlobalId]);
            SetIsInFilter(itemMeta, iFilter, isInFilter);
            if (isInFilter)
            {
                // If the item is in the filter, set the move version to the change version for the item.
                // Otherwise, leave the move version as (0,0).
                SetMoveVersion(itemMeta, iFilter, itemMeta.ChangeVersion);
            }
        }

        // Update the list of filters that are stored in the custom replica metadata.
        AddressFilter.StoreFiltersInReplicaMetadata(ContactReplicaMetadata, TrackedFilters);
    }

    return filterIsNew;
}

// Gets the list of address filters that are tracked by this replica.
public List<AddressFilter> TrackedFilters
{
    get
    {
        return _trackedFilters;
    }
}
private List<AddressFilter> _trackedFilters;

É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 également permettre à votre fournisseur de représenter un réplica filtré lorsqu'il est le fournisseur de destination. Pour plus d'informations sur la façon d'implémenter un fournisseur filtré, consultez Procédure : filtrer un réplica.

Voir aussi

Autres ressources

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

Filtrage des données de synchronisation