다음을 통해 공유


방법: 복제본 필터링

이 항목에서는 필터링된 복제본을 나타내는 Sync Framework 공급자를 구현하기 위해 관리되는 언어를 사용하는 방법을 보여 줍니다. 필터링된 복제본은 필터에 있는 항목 또는 변경 단위의 데이터만 저장합니다.

이 항목에서는 기본적인 C# 및 Microsoft .NET Framework 개념에 익숙하다고 가정합니다.

이 항목의 예제에서는 다음과 같은 Sync Framework 클래스 및 멤버를 중점적으로 설명합니다.

필터링된 복제본 이해

필터링된 복제본은 필터에 있는 항목과 변경 단위에 대해서만 항목과 변경 단위 데이터를 저장하고, 이전에 필터에 포함되었다가 현재는 제외된 항목과 변경 단위(삭제할 항목)에 대해서는 메타데이터를 저장합니다. 또한, 필터링된 복제본은 해당 필터를 추적하며 경우에 따라 다른 필터도 추적할 수 있습니다. 필터링된 복제본은 원본 공급자와 함께 필터를 협상할 수 있으며, 이 경우 원본 공급자가 필터링된 변경 내용 일괄 처리를 생성합니다. 원본 공급자가 필터링된 변경 내용 일괄 처리를 생성할 수 없는 경우에는 필터링된 공급자가 변경 내용 자체를 필터링하여 현재 필터에 있는 변경 내용만 적용할 수 있습니다.

필터링된 공급자는 필터와 관련하여 이동된 항목에 대하여 변경 내용 적용자와 통신하기 위해 IFilteredReplicaNotifyingChangeApplierTarget을 구현합니다. 필터링된 공급자는 원본 공급자가 변경 내용을 열거하기 위해 사용하는 필터를 협상할 수 있도록 일반적으로 IRequestFilteredSync도 구현합니다.

빌드 요구 사항

예제

이 항목의 예제 코드에서는 필터링된 대상 공급자를 구현하는 방법을 보여 줍니다. 필터링된 공급자는 삭제할 항목을 포함하는 필터링된 일괄 변경 내용을 요청하고 필터링된 변경 내용과 삭제할 항목을 대상 복제본에 적용합니다. 이 예제의 복제본은 연락처 정보를 쉼표로 구분된 값의 목록으로 저장하는 텍스트 파일입니다. 동기화할 항목은 이 파일에 포함된 연락처입니다. 필터는 필터 문자열이 연락처의 주소 필드에서 발견되는 경우에만 연락처가 포함되도록 하는 문자열입니다.

필터 협상

Sync Framework에서 대상 복제본의 SpecifyFilter 메서드를 호출하면 대상 복제본이 원본 공급자가 변경 내용을 열거하기 위해 사용하는 필터를 요청합니다. 이 예제에서는 대상 복제본에 의해 추적되는 필터의 목록에서 첫 번째 필터를 지정하고 원본 공급자가 필터를 거부하는 경우 예외를 발생시킵니다.

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

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

필터링된 변경 내용 적용

대상 공급자는 변경 내용 적용자를 사용하여 일괄 변경 내용을 처리합니다. Metadata Storage Service가 사용자 지정 필터링을 지원하지 않으므로 목록을 변경 내용 적용자에게 전송하기 전에 삭제할 항목 변경 내용을 표시하도록 로컬 버전의 목록을 업데이트해야 합니다.

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

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

변경 내용 적용자가 SaveItemChange 메서드를 호출하여 변경 내용을 저장합니다. 필터링된 복제본은 삭제할 항목에 영향을 주는 변경 내용 동작을 처리합니다.

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

    break;
}

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

    break;
}

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

    break;
}

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

    break;
}
public void UpdateGhostFromSync(ItemChange itemChange, FilterKeyMap providerFilterKeyMap)
{
    // Find the ghost metadata in our list or load it from the metadata store.
    ItemMetadata itemMeta = null;
    if (_ContactGhostMetaList.ContainsKey(itemChange.ItemId))
    {
        itemMeta = _ContactGhostMetaList[itemChange.ItemId];
    }
    else
    {
        itemMeta = _ContactReplicaMetadata.FindItemMetadataById(itemChange.ItemId);
    }
    
    // The ghost does not exist, so create it and add it to the metadata store.
    if (null == itemMeta)
    {
        itemMeta = _ContactReplicaMetadata.CreateItemMetadata(itemChange.ItemId,
            itemChange.CreationVersion);

        InitializeFilterTrackingFields(itemMeta);

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

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

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

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

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

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

    return isGhost;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

필터에 포함된 항목 열거

필터링된 공급자는 필터와 관련하여 이동된 항목에 대하여 변경 내용 적용자와 통신하기 위해 IFilteredReplicaNotifyingChangeApplierTarget을 구현합니다. 이 예제는 메타데이터 저장소에 있는 모든 항목을 열거하고 항목이 복제본에 대한 필터에 있는 경우 항목을 반환된 목록에 추가하며 지정된 기본 정보에 포함되어 있지 않은 항목의 버전을 이동합니다.

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

다음 단계

다음으로, 공급자가 대상 공급자와 통신하여 변경 내용 열거에 사용할 필터를 설정할 수 있도록 필터 협상 기능을 추가할 수 있습니다. 필터를 협상하는 방법에 대한 자세한 내용은 방법: 필터 협상을 참조하십시오.

공급자가 필터를 추적하도록 설정할 수도 있습니다. 필터 추적 복제본은 변경 내용을 필터링된 복제본에 보낼 때 정보의 크기를 작게 유지합니다. 필터 추적 공급자를 구현하는 방법에 대한 자세한 내용은 방법: 필터 추적 및 필터링된 변경 내용 열거을 참조하십시오.

참고 항목

개념

일반적인 표준 사용자 지정 공급자 태스크 프로그래밍
동기화 데이터 필터링