Freigeben über


Vorgehensweise: Filtern eines Replikats

In diesem Thema wird beschrieben, wie mit einer verwalteten Sprache ein Sync Framework-Anbieter implementiert wird, der ein gefiltertes Replikat darstellt. Ein gefiltertes Replikat speichert Daten nur für Elemente oder Änderungseinheiten, die sich im Filter befinden.

Dieses Thema setzt grundlegende Kenntnisse über C#- und Microsoft .NET Framework-Konzepte voraus.

In den Beispielen dieses Themas liegt der Schwerpunkt auf der folgenden Klasse und dem folgenden Member von Sync Framework:

Grundlegendes zu gefilterten Replikaten

Ein gefiltertes Replikat speichert Daten für Elemente und Änderungseinheiten nur für solche Elemente und Änderungseinheiten, die sich in seinem Filter befinden, sowie für inaktive Elemente, die Metadaten für Elemente und Änderungseinheiten darstellen, die sich im Filter befunden haben, aber verschoben wurden. Ein gefiltertes Replikat verfolgt seinen eigenen Filter nach. Es kann auch andere Filter nachverfolgen. Ein gefiltertes Replikat kann mit dem Quellenanbieter einen Filter aushandeln. In diesem Fall erstellt der Quellenanbieter einen gefilterten Änderungsbatch. Wenn der Quellenanbieter keinen gefilterten Änderungsbatch erstellen kann, kann der gefilterte Anbieter selbst die Änderungen filtern und nur jene anwenden, die sich in seinem Filter befinden.

Ein gefilterter Anbieter implementiert IFilteredReplicaNotifyingChangeApplierTarget, um mit dem Änderungsanwender über Elemente zu kommunizieren, die im Verhältnis zum Filter verschoben wurden. Ein gefilterter Anbieter implementiert normalerweise auch IRequestFilteredSync, sodass der Filter ausgehandelt werden kann, der vom Quellenanbieter zum Auflisten von Änderungen verwendet wird.

Erstellungsanforderungen

Beispiel

Der Beispielcode in diesem Thema verdeutlicht, wie ein gefilterter Zielanbieter implementiert wird. Der gefilterte Anbieter fordert einen gefilterten Änderungsbatch an, der inaktive Elemente umfasst, und übernimmt die gefilterten Änderungen und inaktiven Elemente für das Zielreplikat. Das Replikat in diesem Beispiel ist eine Textdatei, die Kontaktinformationen als Liste mit durch Trennzeichen getrennten Werten speichert. Die zu synchronisierenden Elemente sind in dieser Datei enthaltene Kontakte. Ein Filter ist eine Zeichenfolge, die dazu führt, dass ein Kontakt nur dann eingeschlossen wird, wenn die Filterzeichenfolge im Adressfeld des Kontakts gefunden wird.

Aushandeln des Filters

Wenn Sync Framework die SpecifyFilter-Methode des Zielreplikats aufruft, fordert das Zielreplikat den Filter an, der vom Quellenanbieter zum Auflisten von Änderungen verwendet wird. In diesem Beispiel wird der erste Filter in der Liste der vom Zielanbieter nachverfolgten Filter festgelegt, und eine Ausnahme wird ausgelöst, wenn der Quellenanbieter den Filter ablehnt.

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

Anwenden von gefilterten Änderungen

Der Zielanbieter verwendet einen Änderungsanwender zum Verarbeiten des Änderungsbatchs. Benutzerdefinierte Filterung wird vom Metadaten-Speicherdienst nicht unterstützt. Daher muss die Liste lokaler Versionen aktualisiert werden, um Änderungen inaktiver Elemente zu markieren, bevor die Liste an den Änderungsanwender gesendet wird.

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

Der Änderungsanwender ruft die SaveItemChange-Methode auf, um Änderungen zu speichern. Ein gefiltertes Replikat behandelt Änderungsaktionen, die inaktive Elemente betreffen.

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

Auflisten von Elementen, die in den Filter verschoben wurden

Der gefilterte Anbieter implementiert IFilteredReplicaNotifyingChangeApplierTarget, um mit dem Änderungsanwender über Elemente zu kommunizieren, die im Verhältnis zum Filter verschoben wurden. In diesem Beispiel werden alle Elemente im Metadatenspeicher aufgelistet. Ein Element wird der zurückgegebenen Liste hinzugefügt, wenn es sich im Filter für das Replikat befindet und die verschobene Version des Elements nicht im angegebenen Basiswissen enthalten ist.

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

Nächste Schritte

Im nächsten Schritt können Sie Ihrem Anbieter eine Filteraushandlung hinzufügen, sodass dieser mit dem Zielanbieter kommunizieren kann, um zu ermitteln, welcher Filter für die Änderungsauflistung verwendet wird. Weitere Informationen zum Aushandeln von Filtern finden Sie unter Vorgehensweise: Aushandeln eines Filters.

Sie können auch ermöglichen, dass der Anbieter Filter nachverfolgt. Filternachverfolgungsreplikate behalten eine kleine Wissensgröße bei, wenn Änderungen an gefilterte Replikate gesendet werden. Weitere Informationen zum Implementieren eines Filternachverfolgungsanbieters finden Sie unter Vorgehensweise: Nachverfolgen von Filtern und Auflisten von gefilterten Änderungen.

Siehe auch

Konzepte

Programmieren allgemeiner benutzerdefinierter Standardanbietertasks
Filtern von Synchronisierungsdaten