Controlar conflictos en proveedores simples
Siempre que sea posible, diseñe aplicaciones en las que se eviten los conflictos. La detección y resolución de conflictos entraña una complejidad, un procesamiento y un tráfico de red adicionales. En algunas aplicaciones, los conflictos no se pueden evitar. Por ejemplo, en una aplicación de personal de ventas, dos vendedores pueden compartir un territorio. Ambos vendedores pueden actualizar los datos para el mismo cliente y los mismos pedidos. Para asegurar que los cambios realizados en los elementos de la comunidad de sincronización se propagan correctamente, el proveedor de destino debe detectar y controlar los conflictos que se producen entre los elementos enviados desde el proveedor de origen y los elementos de la réplica de destino. Sync Framework proporciona objetos que realizan la mayor parte del trabajo necesario para detectar y controlar conflictos.
Sync Framework detecta los conflictos en el nivel del elemento o la unidad de cambio. Sync Framework reconoce dos categorías de conflictos que pueden producirse durante la sincronización: los conflictos de simultaneidad y los conflictos de restricción. Los conflictos de simultaneidad se producen cuando se cambia el mismo elemento o la misma unidad de cambio en dos réplicas distintas que se sincronizan posteriormente. Los conflictos de restricción son los que infringen las restricciones aplicadas a los elementos o las unidades de cambio, como la relación de las carpetas o la ubicación de los datos con un nombre idéntico dentro de un sistema de archivos. Sync Framework divide los conflictos de restricción en las tres categorías siguientes.
Un conflicto de colisión se produce cuando el elemento no puede guardarse porque está en conflicto con otro elemento del almacén de destino, como por ejemplo, cuando el proveedor de origen envía un archivo que tiene el mismo nombre y ubicación que un archivo que ya existe en la réplica de destino.
Un conflicto de elemento primario perdido se produce cuando un elemento no se puede guardar en un almacén de datos jerárquico porque requiere un elemento primario que no existe, como por ejemplo, cuando el proveedor de origen envía un archivo para que se guarde en un directorio que no existe en la réplica de destino.
Otros conflictos de restricción se producen cuando el elemento que se va a guardar infringe una restricción de la réplica de destino, como por ejemplo, cuando el proveedor de origen envía un archivo que es demasiado grande para guardarse en la réplica de destino o cuando el cambio infringe alguna lógica de negocio en la réplica de destino.
Las restricciones estás relacionadas con las capacidades concretas de un almacén de elementos, como por ejemplo, las restricciones de clave externa que son comunes en las bases de datos. Los proveedores simples solamente admiten conflictos de restricción de colisión. Para obtener más información acerca del control de conflictos para proveedores personalizados estándar, vea Detectar y resolver conflictos de restricción.
Introducción al control de conflictos
Para decidir cómo se controlan los conflictos de simultaneidad y de restricción, debe responder a dos preguntas importantes:
¿Los conflictos deben resolverse automáticamente durante la sincronización o debe enviarse una notificación a la aplicación cuando se detecta un conflicto para que pueda controlar su resolución?
¿Para resolver todos los conflictos es necesario que se especifique la victoria del origen o destino, o es necesario un mecanismo de control de conflictos más complejo? Por ejemplo, en un conflicto de simultaneidad, es posible que desee combinar datos del origen y del destino en un solo elemento que se aplique a ambas réplicas.
Después de responder a estas preguntas, puede especificar el modo en que Sync Framework debe comportarse cuando encuentre un conflicto:
Especifique una directiva de resolución para los conflictos de simultaneidad y los conflictos de restricción de colisión. La directiva determina si Sync Framework resuelve el conflicto automáticamente o si, de forma predeterminada, la aplicación responde a un evento para controlar el conflicto.
Código administrado: el proveedor especifica valores de las enumeraciones ConflictResolutionPolicy y CollisionConflictResolutionPolicy del objeto KnowledgeSyncProviderConfiguration que se exponen mediante la propiedad Configuration.
Código no administrado: cuando una aplicación inicia una sesión de sincronización, se pasa CONFLICT_RESOLUTION_POLICY a ISyncSession::Start. En esta versión, no se puede pasar COLLISION_CONFLICT_RESOLUTION_POLICY en el código no administrado.
Si especifica una directiva diferente de la predeterminada, Sync Framework establece la acción de resolución de conflictos adecuada cuando se produce un conflicto. Por ejemplo, si se especifica una directiva de "el origen gana" para los conflictos de simultaneidad, la acción "el origen gana" se establece si se detecta un conflicto de este tipo durante una sesión de sincronización. Si acepta el valor predeterminado de una o ambas directivas de resolución, el proveedor o la aplicación debe responder a los eventos que se desencadenan cuando se detecta un conflicto. El proveedor puede responder implementando los métodos siguientes:
Código administrado: OnItemConflicting y OnItemConstraint
Código no administrado: ISimpleSyncEvents::OnConcurrencyConflict e ISimpleSyncEvents::OnConstraintConflict.
Si el proveedor no implementa estos métodos, se utilizan las devoluciones de llamadas de la aplicación siguientes para que la aplicación pueda establecer la acción de resolución. Si la aplicación no responde a estos eventos, la resolución del conflicto se difiere hasta una sesión de sincronización posterior. En este caso, el conflicto nunca se resolverá a menos que se modifiquen los datos en conflicto o la aplicación.
Código administrado: ItemConflicting y ItemConstraint
Código no administrado: ISyncCallback::OnConflict y ISyncConstraintCallback::OnConstraintConflict.
En respuesta al conflicto, el proveedor o la aplicación debe establecer una acción de resolución.
Código administrado: llama a SetResolutionAction y pasa un valor de ConflictResolutionAction o llama a SetResolutionAction y pasa un valor de ConstraintConflictResolutionAction.
Código no administrado: pasa a IChangeConflict::SetResolveActionForChange o IChangeConflict::SetResolveActionForChangeUnit y pasa un valor de SYNC_RESOLVE_ACTION, o bien llama a IConstraintConflict::SetConstraintResolveActionForChange o IConstraintConflict::GetConstraintResolveActionForChangeUnit y pasa un valor de SYNC_CONSTRAINT_RESOLVE_ACTION.
Además de establecer la acción de resolución, también puede incluir código personalizado en el controlador de eventos. Por ejemplo, puede mostrar los elementos en conflicto de una interfaz de usuario a medida que se procesan.
En algunas de las acciones de resolución que Sync Framework o la aplicación establece, debe implementar una de las interfaces siguientes o las dos:
Código administrado: ISimpleSyncProviderConcurrencyConflictResolver y ISimpleSyncProviderConstraintConflictResolver.
Código no administrado: ISimpleSyncProviderConcurrencyConflictResolver e ISimpleSyncProviderConstraintConflictResolver.
En los conflictos de simultaneidad, los métodos de resolución que se implementan para estas interfaces se distinguen por el tipo de conflictos al que responden, por ejemplo, un conflicto de actualización-actualización. En los conflictos de restricción, los métodos de resolución que se implementan se distinguen por los resultados de la resolución, por ejemplo, el cambio de nombre del elemento de origen.
En los conflictos de simultaneidad, si la acción se establece en Merge (en el código administrado) o SRA_MERGE (en el código no administrado), debe implementar los métodos siguientes que administran los tres tipos de conflictos de simultaneidad:
Código administrado: ResolveUpdateUpdateConflict, ResolveLocalDeleteRemoteUpdateConflict y ResolveLocalUpdateRemoteDeleteConflict.
Código no administrado: ISimpleSyncProviderConcurrencyConflictResolver::ResolveUpdateUpdateConflict, ISimpleSyncProviderConcurrencyConflictResolver::ResolveLocalDeleteRemoteUpdateConflict e ISimpleSyncProviderConcurrencyConflictResolver::ResolveLocalUpdateRemoteDeleteConflict.
La implementación debería combinar los elementos en conflictos de un modo que sea apropiado para la réplica y la aplicación, siempre que haya un elemento final que represente a los dos elementos en conflicto.
En los conflictos de restricción de colisión, los métodos se implementan en función de las acciones que se pueden establecer:
Código administrado (ConstraintConflictResolutionAction):
Código no administrado (SYNC_CONSTRAINT_RESOLVE_ACTION):
Acción MétodoSCRA_RENAME_DESTINATIONISimpleSyncProviderConstraintConflictResolver::ModifyAndUpdateRemoteItem e ISimpleSyncProviderConstraintConflictResolver::ModifyAndInsertRemoteItemSCRA_RENAME_SOURCEISimpleSyncProviderConstraintConflictResolver::ModifyLocalItem
Ejemplo de código administrado
En este ejemplo, las directivas de administración de conflictos de simultaneidad y restricción se mantienen como valor predeterminado de ApplicationDefined
. Esto significa que la aplicación se registrará para los eventos ItemConstraint y ItemConflicting, y especificará una acción para resolver conflictos si se producen durante el procesamiento de sincronización. En el ejemplo de código siguiente se muestran los controladores de eventos que se especifican en el constructor de MyFullEnumerationSimpleSyncProvider
:
this.ItemConstraint += new EventHandler<SimpleSyncItemConstraintEventArgs>(OnItemConstraint);
this.ItemConflicting += new EventHandler<SimpleSyncItemConflictingEventArgs>(OnItemConflicting);
AddHandler Me.ItemConstraint, AddressOf HandleItemConstraint
En el ejemplo de código siguiente se muestran los controladores de eventos que establecen las acciones de resolución de conflictos en Merge
:
void OnItemConstraint(object sender, SimpleSyncItemConstraintEventArgs e)
{
// Set the resolution action for constraint conflicts.
// In this sample, the provider checks for duplicates in InsertItem, and this event would
// fire if a duplicate occurred.
e.SetResolutionAction(ConstraintConflictResolutionAction.Merge);
}
void OnItemConflicting(object sender, SimpleSyncItemConflictingEventArgs e)
{
// Set the resolution action for concurrency conflicts.
e.SetResolutionAction(ConflictResolutionAction.Merge);
}
Private Sub HandleItemConstraint(ByVal sender As Object, ByVal e As SimpleSyncItemConstraintEventArgs)
' Set the resolution action for constraint conflicts.
' In this sample, the provider checks for duplicates in InsertItem, and this event would
' fire if a duplicate occurred.
e.SetResolutionAction(ConstraintConflictResolutionAction.Merge)
End Sub
Private Sub HandleItemConflicting(ByVal sender As Object, ByVal e As SimpleSyncItemConflictingEventArgs)
' Set the resolution action for concurrency conflicts.
e.SetResolutionAction(ConflictResolutionAction.Merge)
End Sub
En el ejemplo de código siguiente se muestra el método MergeConstraintConflict que se implementa para responder a una acción de resolución de Merge en un conflicto de restricción:
public void MergeConstraintConflict(object itemData,
ConflictVersionInformation conflictVersionInformation,
IEnumerable<SyncId> changeUnitsToMerge,
ItemFieldDictionary localConflictingItem,
ItemFieldDictionary keyAndExpectedVersion,
RecoverableErrorReportingContext recoverableErrorReportingContext,
out ItemFieldDictionary updatedKeyAndVersion)
{
ItemTransfer transfer = (ItemTransfer)itemData;
ItemData dataCopy = new ItemData(transfer.ItemData);
// Combine the conflicting data.
ItemData mergedData = (_store.Get(transfer.Id)).Merge((ItemData)dataCopy);
// We are doing a merge so we must delete the old conflicting item from our store.
ulong idConflicting = (ulong)localConflictingItem[CUSTOM_FIELD_ID].Value;
_store.DeleteItem(idConflicting);
// Now create the new merged data in the store.
if (_store.Contains(transfer.Id))
{
_store.UpdateItem(transfer.Id, dataCopy);
}
else
{
_store.CreateItem(mergedData, transfer.Id);
}
updatedKeyAndVersion = _store.CreateItemFieldDictionary(transfer.Id);
}
Public Sub MergeConstraintConflict(ByVal itemData As Object, ByVal conflictVersionInformation As ConflictVersionInformation, ByVal changeUnitsToMerge As IEnumerable(Of SyncId), ByVal localConflictingItem As ItemFieldDictionary, ByVal keyAndExpectedVersion As ItemFieldDictionary, ByVal recoverableErrorReportingContext As RecoverableErrorReportingContext, _
ByRef updatedKeyAndVersion As ItemFieldDictionary) Implements ISimpleSyncProviderConstraintConflictResolver.MergeConstraintConflict
Dim transfer As ItemTransfer = DirectCast(itemData, ItemTransfer)
Dim dataCopy As New ItemData(transfer.ItemData)
' Combine the conflicting data.
Dim mergedData As ItemData = (_store.[Get](transfer.Id)).Merge(DirectCast(dataCopy, ItemData))
' We are doing a merge so we must delete the old conflicting item from our store.
Dim idConflicting As ULong = CULng(localConflictingItem(CUSTOM_FIELD_ID).Value)
_store.DeleteItem(idConflicting)
' Now create the new merged data in the store.
If _store.Contains(transfer.Id) Then
_store.UpdateItem(transfer.Id, dataCopy)
Else
_store.CreateItem(mergedData, transfer.Id)
End If
updatedKeyAndVersion = _store.CreateItemFieldDictionary(transfer.Id)
End Sub
Vea también
Conceptos
Implementar un proveedor simple personalizado
Crear un proveedor simple administrado