Manipulando conflitos para provedores simples
Se for possível, crie aplicativos de modo a evitar conflitos. A detecção e a solução de conflitos representam complexidade, processamento e tráfego de rede adicionais. Em alguns aplicativos, os conflitos não podem ser evitados. Por exemplo, em um aplicativo de força de vendas, dois vendedores podem dividir um território. Os dois podem atualizar os dados para o mesmo cliente e as mesmas ordens. Para assegurar que as alterações feitas em itens na comunidade de sincronização sejam propagadas corretamente, o provedor de destino deverá detectar e manipular conflitos que ocorrem entre itens enviados do provedor de origem e itens na réplica de destino. O Sync Framework fornece objetos que executam a maior parte do trabalho necessário para detectar e tratar conflitos.
O Sync Framework detecta conflitos no nível do item ou da unidade de alteração. O Sync Framework reconhece duas categorias de conflitos que podem ocorrer durante a sincronização: conflitos de simultaneidade e conflitos de restrição. Os conflitos de simultaneidade ocorrem quando o mesmo item ou a mesma unidade de alteração é alterada em duas réplicas diferentes que são sincronizadas posteriormente. Os conflitos de restrição são aqueles que violam as restrições aplicadas a itens ou unidades de alteração, como o relação de pastas ou o local de dados com nomes idênticos em um sistema de arquivos. O Sync Framework divide conflitos de restrição nos três seguintes tipos.
Um conflito de colisão ocorre quando o item não pode ser salvo porque está em conflito com outro item no repositório de destino. Por exemplo, quando o provedor de origem envia um arquivo com os mesmos nome e local de um arquivo existente na réplica de destino.
Um conflito de pai ausente ocorre quando um item não pode ser salvo em um repositório de dados hierárquico porque ele requer um item pai que não existe. Por exemplo, quando o provedor de origem envia um arquivo a ser salvo em um diretório que não existe na réplica de destino.
Outros conflitos de restrição ocorrem quando o item a ser salvo viola uma restrição da réplica de destino. Por exemplo, quando o provedor de origem envia um arquivo muito grande para ser salvo na réplica de destino ou quando a alteração viola alguma lógica de negócios na réplica de destino.
As restrições estão relacionadas aos recursos específicos de um repositório de itens, como restrições de chave estrangeira que são comuns em bancos de dados. Os provedores simples só oferecem suporte para conflitos de restrição de colisão. Para obter mais informações sobre como manipular conflitos para provedores personalizados padrão, consulte Detectando e solucionando conflitos de restrição.
Noções básicas sobre manipulação de conflitos
Para decidir como manipular conflitos de simultaneidade e conflitos de restrição, você deve responder a duas perguntas importantes:
Os conflitos devem ser resolvidos automaticamente durante a sincronização ou o aplicativo deve ser notificado quando um conflito é detectado para que possa acionar a resolução de conflitos?
Todos os conflitos devem ser resolvidos por meio da especificação de que a origem ou o destino prevaleçam ou é necessária uma manipulação de conflitos mais sofisticada? Por exemplo, em um conflito de simultaneidade, convém mesclar os dados de origem e destino em um único item aplicado a ambas as réplicas.
Depois de responder a essas perguntas, você pode especificar como o Sync Framework deve se comportar quando encontrar um conflito:
Especifique uma política de resolução para conflitos de simultaneidade e conflitos de restrição de colisão. A política determina se o Sync Framework resolve automaticamente o conflito ou se o aplicativo assume como padrão a resposta a um evento para manipular o conflito.
Código gerenciado: o provedor especifica valores de enumerações ConflictResolutionPolicy e CollisionConflictResolutionPolicy para o objeto KnowledgeSyncProviderConfiguration que é exposto pela propriedade Configuration.
Código não gerenciado: quando o aplicativo inicia uma sessão de sincronização, o valor CONFLICT_RESOLUTION_POLICY é passado para ISyncSession::Start. Nesta versão, você não pode passar o valor COLLISION_CONFLICT_RESOLUTION_POLICY para código não gerenciado.
Se você especificar uma política diferente da padrão, o Sync Framework definirá a ação de resolução de conflito apropriada quando um conflito ocorrer. Por exemplo, se você especificar uma política do tipo "origem prevalece" para conflitos de simultaneidade, a ação "origem prevalece" será definida se um conflito desse tipo for detectado durante uma sessão de sincronização. Se você aceitar o padrão para uma ou para ambas as políticas de resolução, o provedor ou o aplicativo deverá responder aos eventos acionados quando um conflito é detectado. O provedor pode responder com a implementação dos seguintes métodos:
Código gerenciado: OnItemConflicting e OnItemConstraint
Código não gerenciado: ISimpleSyncEvents::OnConcurrencyConflict e ISimpleSyncEvents::OnConstraintConflict.
Se o provedor não implementar esses métodos, os seguintes retornos de chamada de aplicativo serão usados para que o aplicativo possa definir a ação de resolução. Se o aplicativo não responder a esses eventos, a resolução dos conflito será adiada até uma sessão de sincronização subsequente. Nessa situação, o conflito nunca será resolvido, a menos que os dados conflitantes ou o aplicativo seja alterado.
Código gerenciado: ItemConflicting e ItemConstraint
Código não gerenciado: ISyncCallback::OnConflict e ISyncConstraintCallback::OnConstraintConflict.
Em resposta ao conflito, o provedor ou o aplicativo deve definir uma ação de resolução.
Código gerenciado: chame SetResolutionAction e passe um valor de ConflictResolutionAction ou chame SetResolutionAction e passe um valor de ConstraintConflictResolutionAction.
Código não gerenciado: chame IChangeConflict::SetResolveActionForChange ou IChangeConflict::SetResolveActionForChangeUnit e passe um valor de SYNC_RESOLVE_ACTION ou chame IConstraintConflict::SetConstraintResolveActionForChange ou IConstraintConflict::GetConstraintResolveActionForChangeUnit e passe um valor de SYNC_CONSTRAINT_RESOLVE_ACTION.
Além de configurar a ação de resolução, você também pode incluir código personalizado no manipulador de eventos. Por exemplo, você pode exibir itens conflitantes em uma interface do usuário quando eles são processados.
Para algumas ações de resolução que o Sync Framework ou o aplicativo define, você deve implementar um das seguintes interfaces ou ambas:
Código gerenciado: ISimpleSyncProviderConcurrencyConflictResolver e ISimpleSyncProviderConstraintConflictResolver.
Código não gerenciado: ISimpleSyncProviderConcurrencyConflictResolver e ISimpleSyncProviderConstraintConflictResolver.
Para conflitos de simultaneidade, os métodos de resolução implementados para essas interfaces são diferenciados pelo tipo de conflitos aos quais respondem, como um conflito de atualização/atualização. Para conflitos de restrição, os métodos de resolução implementados são diferenciados pelo resultado da resolução. Por exemplo, renomear o item de origem.
Para conflitos de simultaneidade, se a ação for definida como Merge (para código gerenciado) ou SRA_MERGE (para código não gerenciado), você deverá implementar os seguintes métodos que manipulam os três tipos de conflitos de simultaneidade:
Código gerenciado: ResolveUpdateUpdateConflict, ResolveLocalDeleteRemoteUpdateConflict e ResolveLocalUpdateRemoteDeleteConflict.
Código não gerenciado: ISimpleSyncProviderConcurrencyConflictResolver::ResolveUpdateUpdateConflict, ISimpleSyncProviderConcurrencyConflictResolver::ResolveLocalDeleteRemoteUpdateConflict e ISimpleSyncProviderConcurrencyConflictResolver::ResolveLocalUpdateRemoteDeleteConflict.
A implementação deve mesclar os itens conflitantes de um modo apropriado para a réplica e o aplicativo, contanto que um item final represente os dois itens conflitantes.
Para conflitos de restrição de colisão, implemente métodos baseados nas ações que podem ser definidas:
Código gerenciado (ConstraintConflictResolutionAction):
Código não gerenciado (SYNC_CONSTRAINT_RESOLVE_ACTION):
Ação MétodoSCRA_RENAME_DESTINATIONISimpleSyncProviderConstraintConflictResolver::ModifyAndUpdateRemoteItem e ISimpleSyncProviderConstraintConflictResolver::ModifyAndInsertRemoteItemSCRA_RENAME_SOURCEISimpleSyncProviderConstraintConflictResolver::ModifyLocalItem
Exemplo de código gerenciado
Neste exemplo, as políticas para manipular conflitos relacionadas a conflitos de simultaneidade e conflitos de restrição são mantidas como o padrão ApplicationDefined
. Isso significa que o aplicativo registrará os eventos ItemConflicting e ItemConstraint e especificará uma ação para resolver conflitos se eles ocorrerem durante o processamento da sincronização. O exemplo de código a seguir mostra os manipuladores de eventos especificados no construtor de MyFullEnumerationSimpleSyncProvider
:
this.ItemConstraint += new EventHandler<SimpleSyncItemConstraintEventArgs>(OnItemConstraint);
this.ItemConflicting += new EventHandler<SimpleSyncItemConflictingEventArgs>(OnItemConflicting);
AddHandler Me.ItemConstraint, AddressOf HandleItemConstraint
O exemplo de código a seguir mostra os manipuladores de eventos definindo as ações de resolução de conflito como 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
O exemplo de código a seguir mostra o método MergeConstraintConflict implementado para responder a uma ação de resolução Mesclar para um conflito de restrição:
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
Consulte também
Conceitos
Implementando um provedor personalizado simples
Como criar um provedor simples gerenciado