Как отслеживать фильтры и перечислять фильтруемые изменения

В этом разделе описано отслеживание фильтров, используемых в сообществе синхронизации Sync Framework, и перечисление фильтруемых пакетов изменений на управляемом языке.

Материал этого раздела предполагает, что читатель владеет основными понятиями языка C# и платформы Microsoft .NET Framework.

В примерах, приведенных в этом разделе, рассматриваются в основном следующие классы и члены платформы Sync Framework.

Основные сведения об отслеживании фильтров

Рекомендуется, чтобы все реплики в сообществе синхронизации отслеживали фильтры, используемые в этом сообществе. Если фильтруемая реплика получает перечисление изменений с фильтрацией от отслеживающей фильтры реплики, набор знаний фильтруемой реплики сохраняет небольшой размер. Если фильтруемая реплика получает перечисление изменений с фильтрацией от реплики, не отслеживающей изменения, размер набора знаний растет пропорционально количеству отправленных изменений.

Отслеживающий фильтры поставщик назначения реализует интерфейс IFilterTrackingProvider. Sync Framework использует его для согласования фильтров, отслеживаемых как в исходной, так и в конечной реплике.

Поставщик источника отправляет метаданные фильтра для всех фильтров, совместно отслеживаемых при синхронизации. Отслеживающий фильтры поставщик обычно реализует интерфейс ISupportFilteredSync и может перечислять изменения, фильтруемые с помощью одного из отслеживаемых фильтров.

Отслеживающий фильтры поставщик реализует интерфейс IFilterTrackingNotifyingChangeApplierTarget, используемый объектом применения изменений для получения и обновления схемы ключей фильтра и фильтрации утраченного набора знаний фильтров. Поставщик назначения также обновляет метаданные изменения фильтров элементов и изменяет элементы, отправленные методом SaveItemChange или SaveChangeWithChangeUnits.

Требования построения

Пример

В примере кода, приведенном в этом разделе, показана реализация отслеживающего фильтры поставщика, который отправляет метаданные изменения фильтров и может перечислить фильтрованный пакет изменения при выполнении роли поставщика источника, а также применяет метаданные изменений фильтров при выполнении роли поставщика назначения. Реплика в этом примере является текстовым файлом, в котором хранится контактная информация в виде списка значений с разделителями-запятыми. Синхронизируемыми элементами являются контакты, содержащиеся в этом файле. Фильтр представляет собой строку, согласно которой контакт включается только при обнаружении строки фильтра в поле адреса контакта.

Согласование отслеживаемых фильтров

Для согласования отслеживаемых фильтров Sync Framework вызывает метод SpecifyTrackedFilters поставщика назначения. В этом примере выполняется перечисление списка фильтров, отслеживаемых конечной репликой, отправка всех фильтров поставщику источника и добавление фильтра к списку совместно отслеживаемых фильтров только в тех случаях, когда поставщик источника также отслеживает фильтр.

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

Для всех фильтров, перечисляемых поставщиком назначения, платформа Sync Framework вызывает метод TryAddTrackedFilter поставщика источника. В этом примере выполняется проверка отслеживания указанного фильтра исходной репликой. Если фильтр отслеживается, то он добавляется к списку совместно отслеживаемых фильтров.

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

Согласование фильтра, используемого для синхронизации

Если конечная реплика является фильтруемой, то она обычно запрашивает фильтр, используемый поставщиком источника для перечисления изменений. Поставщик источника получает этот запрос с помощью метода TryAddFilter(Object). В этом примере выполняется проверка того, является ли запрашиваемый фильтр совместно отслеживаемым. Если да, то он сохраняется для использования при изменении перечисления.

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

Отправка метаданных фильтров и фильтруемых изменений

Поставщик источника отправляет метаданные фильтра при отправке пакета изменений. Этот пример может быть использован для отправки либо фильтруемого, либо нефильтруемого пакета изменений в зависимости от того, использовался ли для синхронизации фильтр. Ко всем изменениям, включенным в пакет изменений, при изменении элементов относительно отслеживаемого фильтра присоединяются метаданные изменений фильтра. Для всех отслеживаемых фильтров также отправляется утраченный набор знаний фильтра. Если для синхронизации используется фильтр, то отправка элемента выполняется при его нахождении в фильтре и пометке перемещенного из фильтра элемента как фантома.

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

Метаданные изменений фильтров проверяются для всех совместно отслеживаемых фильтров. Метаданные изменения фильтров добавляются к изменению только в том случае, если версия перемещения изменения не содержится в наборе знаний назначения.

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

Применение метаданных фильтра

Изменения применяются с помощью объекта применения изменений, который вызывает метод поставщика назначения SaveItemChange. При создании и обновлении элемента в этом примере создаются и обновляются данные в хранилище контактов, обновляются метаданные синхронизации и обновляются метаданные отслеживания фильтров. Метаданные отслеживания фильтров обновляются для всех фильтров, отслеживаемых поставщиком назначения, а не только для совместно отслеживаемых фильтров. Если метаданные фильтров отправлены поставщиком источника, то они используются. В противном случае изменения сравниваются со всеми отслеживаемыми фильтрами и настраиваются необходимые метаданные изменения фильтров.

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

Объект применения изменений также вызывает метод интерфейса IFilterTrackingNotifyingChangeApplierTarget поставщика назначения для получения и сохранения метаданных отслеживания фильтров. В этом примере возвращаются запрошенные объекты и сохраняются указанные метаданные.

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

Сохранение метаданных фильтра

Фильтры, отслеживаемые репликой, должны быть сохранены в реплике вместе с утраченным набором знаний для всех отслеживаемых фильтров. В этом примере для сохранения метаданных используется служба хранилища метаданных. Служба хранилища метаданных не поддерживает пользовательские фильтры, поэтому отслеживаемые фильтры сериализуются в виде потока байтов и сохраняются в пользовательском поле метаданных реплики хранилища метаданных.

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

Все элементы отслеживают свое включение в фильтр и версию изменения, после которой элемент был перемещен из фильтра или в фильтр. В этом примере метаданные отслеживания фильтра хранятся для элемента в виде пользовательских полей элементов в хранилище метаданных.

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

Следующие шаги

Затем к поставщику необходимо добавить согласование фильтров для поддержки его взаимодействия с поставщиком назначения для определения фильтра, который будет использоваться для перечисления изменений. Дополнительные сведения о согласовании фильтров см. в разделе Как согласовать фильтр.

Можно также реализовать для поставщика возможность представления фильтруемой реплики при ее нахождении в поставщике назначения. Дополнительные сведения о реализации поставщика с фильтрацией см. в разделе Как фильтровать реплики.

См. также

Другие ресурсы

Программирование типовых задач стандартных пользовательских поставщиков

Фильтрация данных синхронизации