フィルターを追跡し、フィルターされた変更を列挙する方法

このトピックでは、マネージ言語を使用して、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 メソッドを介してこの要求を受け取ります。この例は、要求されたフィルターが双方で追跡されるフィルターであるかどうかをチェックし、双方で追跡される場合は変更を列挙するときに使用するためにフィルターを保存します。

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

フィルター メタデータの格納

レプリカによって追跡されるフィルターは、追跡される各フィルターの忘れられたナレッジと共にレプリカに格納する必要があります。この例は、Metadata Storage Service を使用してメタデータを格納します。Metadata Storage Service はカスタム フィルターをサポートしないので、追跡されるフィルターはバイト ストリームにシリアル化されてメタデータ ストアのカスタム レプリカ メタデータ フィールドに格納されます。

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;

次の手順

次は、フィルター ネゴシエーションをプロバイダーに追加して、変更列挙にどのフィルターを使用するかを同期先プロバイダーと通信して決定できるようにします。フィルターのネゴシエーションの詳細については、「フィルター ネゴシエーションの方法」を参照してください。

また、必要に応じて、プロバイダーが同期先プロバイダーである場合に、フィルターされたレプリカをプロバイダーで表すようにします。フィルターされたプロバイダーを実装する方法の詳細については、「レプリカをフィルターする方法」を参照してください。

参照

概念

標準のカスタム プロバイダーをプログラミングするための一般的なタスク
同期データのフィルター設定