Compartir a través de


Realizar el seguimiento de filtros y enumerar los cambios filtrados

En este tema se describen la forma de usar un lenguaje administrado para el seguimiento de los filtros que se usan en una comunidad de sincronización de Sync Framework y la forma de enumerar un lote de cambios filtrados.

En este tema se presupone que se dispone de un conocimiento básico de C# y conceptos de Microsoft .NET Framework.

Los ejemplos de este tema se centran en las siguientes clases y miembros de Sync Framework:

Descripción del seguimiento de filtros

Se recomienda que todas las réplicas en una comunidad de sincronización realicen el seguimiento de los filtros que se utilizan en la comunidad. Cuando una réplica filtrada recibe una enumeración de cambios filtrada de una réplica de seguimiento de filtros, el conocimiento de la réplica filtrada se mantiene pequeño. Cuando una réplica filtrada recibe una enumeración de cambios filtrados de una réplica que no realiza el seguimiento del filtro, el tamaño del conocimiento crece proporcionalmente al número de cambios enviados.

Un proveedor de seguimiento de filtros implementa la interfaz IFilterTrackingProvider. Sync Framework usa esta interfaz para mediar en la negociación de qué filtros son objeto de seguimiento por la réplica de origen y la réplica de destino.

El proveedor de origen envía los metadatos de filtro para cada filtro objeto de seguimiento mutuo durante la sincronización. Normalmente, un proveedor de seguimiento de filtros también implementa la interfaz ISupportFilteredSync y puede enumerar los cambios filtrados por cualquiera de los filtros objeto de seguimiento.

El proveedor de destino implementa la interfaz IFilterTrackingNotifyingChangeApplierTarget, que el aplicador de cambios utiliza para obtener y actualizar el mapa de claves de filtro y el conocimiento olvidado de filtro de los filtros objeto de seguimiento. El proveedor de destino también actualiza los metadatos de cambio de filtro de los elementos y las unidades de cambio que se envían al método SaveItemChange o SaveChangeWithChangeUnits.

Requisitos de la compilación

Ejemplo

En el ejemplo de código de este tema se muestra la forma de implementar un proveedor de seguimiento de filtros que envía los metadatos de cambio de filtro y puede enumerar un lote de cambios filtrados cuando actúa como proveedor de origen, y que aplica los metadatos de cambio de filtro cuando actúa como proveedor de destino. La réplica de este ejemplo es un archivo de texto que almacena información de contacto en forma de lista de valores separados por comas. Los elementos para sincronizar son los contactos que contiene este archivo. Un filtro es una cadena que hace que un contacto se incluya solamente si la cadena del filtro se encuentra en el campo de dirección del contacto.

Negociar los filtros objeto de seguimiento

Para negociar los filtros objeto de seguimiento, Sync Framework llama a SpecifyTrackedFilters en el proveedor de destino. En este ejemplo se enumera la lista de filtros objeto de seguimiento por la réplica de destino, se envía cada filtro al proveedor de origen y, a continuación, se agrega el filtro a la lista de filtros objeto de seguimiento mutuo solamente si el proveedor de origen realiza también el seguimiento del filtro.

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);
        }
    }
}

Para cada filtro enumerado por el proveedor de destino, Sync Framework llama al método TryAddTrackedFilter del proveedor de origen. En este ejemplo se comprueba si el filtro especificado es objeto de seguimiento por la réplica de origen y, en caso afirmativo, lo agrega a la lista de filtros objeto de seguimiento mutuo.

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;
}

Negociar el filtro utilizado para la sincronización

Cuando la réplica de destino es una réplica filtrada, normalmente solicita el filtro que el proveedor de origen utiliza para enumerar los cambios. El proveedor de origen recibe esta solicitud mediante el método TryAddFilter. En este ejemplo se comprueba si el filtro solicitado es un filtro objeto de seguimiento mutuo y, en caso afirmativo, se guarda para usarlo al enumerar los cambios.

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);
}

Enviar los metadatos de filtro y los cambios filtrados

El proveedor de origen envía los metadatos de filtro cuando envía un lote de cambios. En este ejemplo se puede enviar un lote de cambios filtrados o un lote de cambios sin filtrar, dependiendo de si el filtro de usa para la sincronización. Cada cambio incluido en el lote de cambios tiene adjuntos los metadatos de cambio de filtro cuando el elemento ha cambiado respecto a un filtro objeto de seguimiento. También se envía el conocimiento olvidado de filtro para cada filtro objeto de seguimiento. Cuando un filtro se usa para la sincronización, un elemento se envía si está en el filtro y un elemento que ha salido del filtro se marca como fantasma.

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;
}

Los metadatos de cambio de filtro se examinan para cada filtro objeto de seguimiento mutuo. Los metadatos de cambio de filtro se agregan a un cambio cuando la versión de movimiento del cambio no está en el conocimiento de destino.

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);
        }
    }
}

Aplicar metadatos de filtro

Los cambios se aplican utilizando un aplicador de cambios, que llama al método SaveItemChange del proveedor de destino. Cuando se crea o actualiza un elemento, en este ejemplo se crean o actualizan los datos del almacén de contactos, y se actualizan tanto los metadatos de sincronización como los metadatos de seguimiento de filtros. Los metadatos de seguimiento de filtros se actualizan para todos los filtros objeto de seguimiento por el proveedor de destino, no solo para los filtros objeto de seguimiento mutuo. Cuando los metadatos de filtro los envía el proveedor de origen, se utilizan; de lo contrario, el cambio se compara con cada filtro objeto de seguimiento y se establecen los metadatos de cambio de filtro apropiados.

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);
    }
}

El aplicador de cambios también llama a los métodos de la interfaz IFilterTrackingNotifyingChangeApplierTarget del proveedor de destino para obtener y guardar los metadatos de seguimiento de filtros. En este ejemplo se devuelven los objetos solicitados y se guardan los metadatos especificados.

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);
}

Almacenar metadatos de filtro

Los filtros objeto de seguimiento por una réplica deben almacenarse en la réplica, junto con el conocimiento olvidado de filtro de cada filtro objeto de seguimiento. En este ejemplo se usa Metadata Storage Service para almacenar los metadatos. Metadata Storage Service no admite filtros personalizados, por lo que los filtros personalizados se serializan en una secuencia de bytes y se almacenan en el campo de metadatos de réplica personalizado del almacén de metadatos.

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;
    }
}

Cada elemento realiza un seguimiento para comprobar si está en un filtro, y la versión del cambio que provocó la inclusión o exclusión del elemento en el filtro. En este ejemplo se almacenan los metadatos de seguimiento de filtros para un elemento como campos de elementos personalizados en el almacén de metadatos.

// 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;

Pasos siguientes

A continuación, puede agregar la negociación de filtros al proveedor de modo que pueda comunicar con el proveedor de destino para establecer el filtro que se ha de usar en la enumeración de cambios. Para obtener más información sobre cómo se negocian los filtros, vea Negociar un filtro.

También puede habilitar el proveedor para que represente una réplica filtrada cuando sea el proveedor de destino. Para obtener más información sobre cómo se implementa un proveedor filtrado, vea Filtrar una réplica.

Vea también

Conceptos

Programar tareas comunes de un proveedor personalizado estándar
Filtrar los datos de sincronización