Filtrar una réplica
En este tema se describe la forma de usar un lenguaje administrado para implementar un proveedor de Sync Framework que representa una réplica filtrada. Una réplica filtrada almacena datos solo para los elementos o unidades de cambio que están en su filtro.
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 el miembro y la clase de Sync Framework siguientes:
Descripción de las réplicas filtradas
Una réplica filtrada almacena datos de elementos y unidades de cambio solo para los elementos y unidades de cambio que están en su filtro, así como fantasmas, que son metadatos para los elementos y unidades de cambio que estaban en el filtro pero se han excluido. Una réplica filtrada también realiza el seguimiento de su filtro y, además, puede realizar el seguimiento de otros filtros. Una réplica filtrada puede negociar un filtro con el proveedor de origen, en cuyo caso el proveedor de origen crea un lote de cambios filtrado. Si el proveedor de origen no puede crear un lote de cambios filtrado, el proveedor filtrado puede filtrar los cambios por sí mismo y aplicar solamente los que estén en su filtro.
Un proveedor filtrado implementa IFilteredReplicaNotifyingChangeApplierTarget para establecer comunicación con el aplicador de cambios acerca de los elementos que han cambiado de situación respecto al filtro. Normalmente, un proveedor filtrado también implementa IRequestFilteredSync para poder negociar el filtro que el proveedor de origen usa para enumerar los cambios.
Requisitos de la compilación
.NET Framework 2.0 o posterior.
Remítase a Microsoft.Synchronization.
Remítase a Microsoft.Synchronization.MetadataStorage.
Ejemplo
En el ejemplo de código de este tema se muestra la forma de implementar un proveedor de destino filtrado. El proveedor filtrado solicita un lote de cambios filtrados que incluye fantasmas, y aplica los cambios filtrados y los fantasmas a la réplica 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 el filtro
Cuando Sync Framework llama al método SpecifyFilter de la réplica de destino, esta solicita el filtro que el proveedor de origen utiliza para enumerar los cambios. En este ejemplo se especifica el primer filtro de la lista de filtros objeto de seguimiento por la réplica de destino y se produce una excepción si el proveedor de origen rechaza el filtro.
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.");
}
}
Aplicar cambios filtrados
El proveedor de destino usa un aplicador de cambios para procesar el lote de cambios. Metadata Storage Service no admite filtros personalizados; por lo tanto, la lista de versiones locales debe actualizarse para marcar los cambios de fantasma antes de que se envíe al aplicador de cambios.
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);
}
El aplicador de cambios llama al método SaveItemChange para guardar los cambios. Una réplica filtrada controla las acciones de cambio que afectan a los fantasmas.
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);
}
Enumerar los elementos incluidos en el filtro
El proveedor filtrado implementa IFilteredReplicaNotifyingChangeApplierTarget para establecer comunicación con el aplicador de cambios acerca de los elementos que han cambiado de situación respecto al filtro. En este ejemplo se enumeran todos los elementos del almacén de metadatos y se agrega un elemento a la lista devuelta cuando el elemento está en el filtro para la réplica y la versión de movimiento del elemento no está en el conocimiento base especificado.
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();
}
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 el seguimiento de los filtros. Las réplicas de seguimiento de filtros mantienen pequeño el tamaño del conocimiento cuando se envían los cambios a las réplicas filtradas. Para obtener más información sobre cómo se implementa un proveedor de seguimiento de filtros, vea Realizar el seguimiento de filtros y enumerar los cambios filtrados.
Vea también
Conceptos
Programar tareas comunes de un proveedor personalizado estándar
Filtrar los datos de sincronización