Como criar um provedor simples gerenciado
Este tópico descreve partes importantes do aplicativo de exemplo "Sync101 using Simple Sync Provider" incluído no Sync Framework SDK. Este aplicativo demonstra como criar e sincronizar provedores simples baseados em âncora e de enumeração completa. O aplicativo de exemplo tem três classes:
MyFullEnumerationSimpleSyncProvider, que deriva de FullEnumerationSimpleSyncProvider.
MyAnchorEnumerationSimpleSyncProvider, que deriva de AnchorEnumerationSimpleSyncProvider.
MySimpleDataStore é um repositório de dados de itens na memória. MySimpleDataStore é uma classe usada para este exemplo; não é uma parte do Sync Framework.
Ambas as classes de provedor implementam as seguintes interfaces de manipulação de conflitos: ISimpleSyncProviderConcurrencyConflictResolver e ISimpleSyncProviderConstraintConflictResolver.
Este tópico descreve as seguintes partes do aplicativo:
Criando o repositório de metadados
Identificando itens no repositório de itens e no repositório de metadados
Enumerando itens e carregando dados
Sincronizando dois provedores
Manipulando conflitos
O tópico descreve brevemente também como filtrar dados e executar exclusões apenas no local.
Criando o repositório de metadados
Cada réplica requer um repositório de metadados, que, para provedores simples, é uma instância do SqlMetadataStore. O exemplo de código a seguir especifica opções para um repositório no construtor do MyFullEnumerationSimpleSyncProvider:
public MyFullEnumerationSimpleSyncProvider(string name, MySimpleDataStore store)
{
_name = name;
_store = store;
// Create a file to store metadata for all items and a file to store
// the replica ID.
_replicaMetadataFile = Environment.CurrentDirectory + "\\" + _name.ToString() + ".Metadata";
_replicaIdFile = Environment.CurrentDirectory + "\\" + _name.ToString() + ".Replicaid";
// Set ReplicaIdFormat to use a GUID as an ID, and ItemIdFormat to use a GUID plus
// an 8-byte prefix.
_idFormats = new SyncIdFormatGroup();
_idFormats.ItemIdFormat.IsVariableLength = false;
_idFormats.ItemIdFormat.Length = 24;
_idFormats.ReplicaIdFormat.IsVariableLength = false;
_idFormats.ReplicaIdFormat.Length = 16;
this.ItemConstraint += new EventHandler<SimpleSyncItemConstraintEventArgs>(OnItemConstraint);
this.ItemConflicting += new EventHandler<SimpleSyncItemConflictingEventArgs>(OnItemConflicting);
}
Public Sub New(ByVal name As String, ByVal store As MySimpleDataStore)
_name = name
_store = store
' Create a file to store metadata for all items and a file to store
' the replica ID.
_replicaMetadataFile = (Environment.CurrentDirectory & "\") + _name.ToString() & ".Metadata"
_replicaIdFile = (Environment.CurrentDirectory & "\") + _name.ToString() & ".Replicaid"
' Set ReplicaIdFormat to use a GUID as an ID, and ItemIdFormat to use a GUID plus
' an 8-byte prefix.
_idFormats = New SyncIdFormatGroup()
_idFormats.ItemIdFormat.IsVariableLength = False
_idFormats.ItemIdFormat.Length = 24
_idFormats.ReplicaIdFormat.IsVariableLength = False
_idFormats.ReplicaIdFormat.Length = 16
AddHandler Me.ItemConstraint, AddressOf HandleItemConstraint
AddHandler Me.ItemConflicting, AddressOf HandleItemConflicting
End Sub
O exemplo de código a seguir cria um repositório.
private void InitializeMetadataStore()
{
SyncId id = ReplicaId;
// Create or open the metadata store, initializing it with the ID formats
// that are used to reference items and replicas.
if (!File.Exists(_replicaMetadataFile))
{
_metadataStore = SqlMetadataStore.CreateStore(_replicaMetadataFile);
}
else
{
_metadataStore = SqlMetadataStore.OpenStore(_replicaMetadataFile);
}
}
Private Sub InitializeMetadataStore()
Dim id As SyncId = ReplicaId
' Create or open the metadata store, initializing it with the ID formats
' that are used to reference items and replicas.
If Not File.Exists(_replicaMetadataFile) Then
_metadataStore = SqlMetadataStore.CreateStore(_replicaMetadataFile)
Else
_metadataStore = SqlMetadataStore.OpenStore(_replicaMetadataFile)
End If
End Sub
O código a seguir retorna este repositório como uma propriedade de provedor:
public override MetadataStore GetMetadataStore(out SyncId replicaId, out System.Globalization.CultureInfo culture)
{
InitializeMetadataStore();
replicaId = ReplicaId;
culture = CultureInfo.CurrentCulture;
return _metadataStore;
}
Public Overrides Function GetMetadataStore(ByRef replicaId__1 As SyncId, ByRef culture As System.Globalization.CultureInfo) As MetadataStore
InitializeMetadataStore()
replicaId__1 = ReplicaId
culture = CultureInfo.CurrentCulture
Return _metadataStore
End Function
Identificando itens no repositório de itens e no repositório de metadados
Para sincronizar um item, o Sync Framework deve poder identificar o item no repositório de itens e mapear essa identidade para uma ID interna no repositório de metadados. Ele também precisa determinar se a versão do item foi alterada desde a última sessão de sincronização. Se a versão tiver sido alterada e a réplica de destino ainda não contiver essa versão de um item, o item deverá ser sincronizado. Se as alterações forem sincronizadas no nível de uma unidade de alteração em vez de um item, o Sync Framework deverá poder identificar a unidade de alteração e sua versão. Uma unidade de alteração representa uma alteração de subitem, como o campo de número de telefone em um item que representa um contato. Este exemplo não usa unidades de alteração.
Especificando o formato de IDs do repositório de metadados
O código a seguir define o construtor para o MyFullEnumerationSimpleSyncProvider e a propriedade IdFormats. Assim, o tempo de execução do Sync Framework pode determinar o formato que o repositório de metadados usa para IDs. Caso não sejam usadas IDs flexíveis, o Sync Framework usará um formato fixo para identificar réplicas, itens e unidades de alteração. Se forem usadas IDs flexíveis, serão usados métodos ISimpleSyncProviderIdGenerator para gerar IDs.
public MyFullEnumerationSimpleSyncProvider(string name, MySimpleDataStore store)
{
_name = name;
_store = store;
// Create a file to store metadata for all items and a file to store
// the replica ID.
_replicaMetadataFile = Environment.CurrentDirectory + "\\" + _name.ToString() + ".Metadata";
_replicaIdFile = Environment.CurrentDirectory + "\\" + _name.ToString() + ".Replicaid";
// Set ReplicaIdFormat to use a GUID as an ID, and ItemIdFormat to use a GUID plus
// an 8-byte prefix.
_idFormats = new SyncIdFormatGroup();
_idFormats.ItemIdFormat.IsVariableLength = false;
_idFormats.ItemIdFormat.Length = 24;
_idFormats.ReplicaIdFormat.IsVariableLength = false;
_idFormats.ReplicaIdFormat.Length = 16;
this.ItemConstraint += new EventHandler<SimpleSyncItemConstraintEventArgs>(OnItemConstraint);
this.ItemConflicting += new EventHandler<SimpleSyncItemConflictingEventArgs>(OnItemConflicting);
}
public SyncId ReplicaId
{
get
{
if (_replicaId == null)
{
_replicaId = GetReplicaIdFromFile( _replicaIdFile);
}
return _replicaId;
}
}
public override SyncIdFormatGroup IdFormats
{
get { return _idFormats; }
}
Public Sub New(ByVal name As String, ByVal store As MySimpleDataStore)
_name = name
_store = store
' Create a file to store metadata for all items and a file to store
' the replica ID.
_replicaMetadataFile = (Environment.CurrentDirectory & "\") + _name.ToString() & ".Metadata"
_replicaIdFile = (Environment.CurrentDirectory & "\") + _name.ToString() & ".Replicaid"
' Set ReplicaIdFormat to use a GUID as an ID, and ItemIdFormat to use a GUID plus
' an 8-byte prefix.
_idFormats = New SyncIdFormatGroup()
_idFormats.ItemIdFormat.IsVariableLength = False
_idFormats.ItemIdFormat.Length = 24
_idFormats.ReplicaIdFormat.IsVariableLength = False
_idFormats.ReplicaIdFormat.Length = 16
AddHandler Me.ItemConstraint, AddressOf HandleItemConstraint
AddHandler Me.ItemConflicting, AddressOf HandleItemConflicting
End Sub
Public ReadOnly Property ReplicaId() As SyncId
Get
If _replicaId Is Nothing Then
_replicaId = GetReplicaIdFromFile(_replicaIdFile)
End If
Return _replicaId
End Get
End Property
Public Overrides ReadOnly Property IdFormats() As SyncIdFormatGroup
Get
Return _idFormats
End Get
End Property
Especificando campos de item e o esquema de metadados
O Sync Framework mapeia dados do repositório de itens ou metadados adicionais criados por você para versões e IDs do repositório de metadados interno usando um objeto ItemMetadataSchema exposto pela propriedade MetadataSchema. Os exemplos de código a seguir fornecem entrada para o objeto ItemMetadataSchema. As constantes no código de exemplo definem um valor inteiro para cada coluna no repositório de itens. Esses valores são usados na criação das definições do campo personalizado e das regras de identidade para o objeto ItemMetadataSchema.
public const uint CUSTOM_FIELD_ID = 1;
public const uint CUSTOM_FIELD_TIMESTAMP = 2;
public override ItemMetadataSchema MetadataSchema
{
get
{
CustomFieldDefinition[] customFields = new CustomFieldDefinition[2];
customFields[0] = new CustomFieldDefinition(CUSTOM_FIELD_ID, typeof(ulong));
customFields[1] = new CustomFieldDefinition(CUSTOM_FIELD_TIMESTAMP, typeof(ulong));
IdentityRule[] identityRule = new IdentityRule[1];
identityRule[0] = new IdentityRule(new uint[] { CUSTOM_FIELD_ID });
return new ItemMetadataSchema(customFields, identityRule);
}
}
Public Const CUSTOM_FIELD_ID As UInteger = 1
Public Const CUSTOM_FIELD_TIMESTAMP As UInteger = 2
Public Overrides ReadOnly Property MetadataSchema() As ItemMetadataSchema
Get
Dim customFields As CustomFieldDefinition() = New CustomFieldDefinition(1) {}
customFields(0) = New CustomFieldDefinition(CUSTOM_FIELD_ID, GetType(ULong))
customFields(1) = New CustomFieldDefinition(CUSTOM_FIELD_TIMESTAMP, GetType(ULong))
Dim identityRule As IdentityRule() = New IdentityRule(0) {}
identityRule(0) = New IdentityRule(New UInteger() {CUSTOM_FIELD_ID})
Return New ItemMetadataSchema(customFields, identityRule)
End Get
End Property
O objeto ItemMetadataSchema expõe três propriedades:
-
Campos personalizados são campos no repositório de metadados identificados por inteiros. Se um aplicativo exigir um nome amigável para um ou mais campos, ele deve mapear o inteiro para um nome. Os campos personalizados são definidos por dois motivos: para identificar itens e para fornecer informações de versão sobre esses itens. Os campos de versão habilitam o Sync Framework a determinar se um item ou uma unidade de alteração foi alterada. Neste exemplo, os campos de versão contêm os dados reais do repositório de itens; assim, há um campo para cada campo no repositório de itens. Esta correspondência um para um não é necessária, nem é eficiente. Uma solução mais prática seria pegar um hash dos campos de item e armazená-lo em um único campo personalizado.
-
A regra de identidade especifica o campo ou os campos personalizados que devem ser usados para identificar um item. Nesse caso, é usado o campo CUSTOM_FIELD_ID (campo 0).
ChangeUnitVersionDefinitions (não usado neste exemplo)
Se forem usadas unidades de alteração, você deve definir campos de versão para as unidades de alteração. Não é necessário que haja um mapeamento um para um entre as unidades de alteração e as informações de versão, ou que os dados reais devam ser armazenados. As unidades de alteração também podem abranger vários campos. Por exemplo, este aplicativo poderia especificar que Zip e Phone são uma unidade de alteração e que Guid é outra unidade de alteração. Para Guid, você pode usar os dados reais e, para a outra unidade de alteração, um hash dos campos Zip e Phone ou algum outro mecanismo para determinar a versão.
Alguns dos métodos que trabalham com dados do repositório de itens, como InsertItem, exigem uma coleção de objetos ItemField que representam cada campo. Os objetos ItemFieldDictionary que são parâmetros para esses métodos têm os mesmos valores de índice que os especificados nos objetos CustomFieldDefinition.
Enumerando itens e carregando dados
O Sync Framework deve poder enumerar itens no repositório de itens de origem, detectar se itens ou unidades de alteração foram alterados e carregar os dados alterados de forma que eles possam ser aplicados ao repositório de destino. A detecção de alteração é tratada pelo tempo de execução do Sync Framework, mas a enumeração de alteração e o carregamento de dados são específicos do repositório e tratados para provedores de enumeração completa por meio da implementação de EnumerateItems e LoadChangeData. O seguinte exemplo de código retorna uma lista de itens que são enumerados do objeto MySimpleDataStore:
public override void EnumerateItems(FullEnumerationContext context)
{
List<ItemFieldDictionary> items = new List<ItemFieldDictionary>();
foreach (ulong id in _store.Ids)
{
items.Add(_store.CreateItemFieldDictionary(id));
}
context.ReportItems(items);
}
Public Overrides Sub EnumerateItems(ByVal context As FullEnumerationContext)
Dim items As New List(Of ItemFieldDictionary)()
For Each id As ULong In _store.Ids
items.Add(_store.CreateItemFieldDictionary(id))
Next
context.ReportItems(items)
End Sub
O seguinte exemplo de código retorna um objeto que contém uma das alterações de dados que foram enumerados por EnumerateItems. O Sync Framework chama esse método até que todas as alterações tenham sido carregadas.
public override object LoadChangeData(ItemFieldDictionary keyAndExpectedVersion, IEnumerable<SyncId> changeUnitsToLoad, RecoverableErrorReportingContext recoverableErrorReportingContext)
{
IDictionary<uint, ItemField> expectedFields = (IDictionary<uint, ItemField>)keyAndExpectedVersion;
ulong id = (ulong)expectedFields[CUSTOM_FIELD_ID].Value;
return new ItemTransfer(id, _store.Get(id));
}
Public Overrides Function LoadChangeData(ByVal keyAndExpectedVersion As ItemFieldDictionary, ByVal changeUnitsToLoad As IEnumerable(Of SyncId), ByVal recoverableErrorReportingContext As RecoverableErrorReportingContext) As Object
Dim expectedFields As IDictionary(Of UInteger, ItemField) = DirectCast(keyAndExpectedVersion, IDictionary(Of UInteger, ItemField))
Dim id As ULong = CULng(expectedFields(CUSTOM_FIELD_ID).Value)
Return New ItemTransfer(id, _store.[Get](id))
End Function
Aplicando inserções, atualizações e exclusões
Depois que o Sync Framework tiver detectado e carregado alterações da origem, ele deverá aplicar essas alterações e as alterações de metadados correspondentes à réplica de destino. As alterações de metadados no destino são manipuladas pelo Sync Framework, mas a aplicação de alterações de dados é específica do repositório e manipulada por meio da implementação destes métodos: DeleteItem, InsertItem e UpdateItem. Os exemplos de código a seguir fornecem uma implementação para cada um desses métodos:
public override void InsertItem(object itemData,
IEnumerable<SyncId> changeUnitsToCreate,
RecoverableErrorReportingContext recoverableErrorReportingContext,
out ItemFieldDictionary keyAndUpdatedVersion,
out bool commitKnowledgeAfterThisItem)
{
ItemTransfer transfer = (ItemTransfer)itemData;
ItemData dataCopy = new ItemData(transfer.ItemData);
// Check for duplicates, and record a constraint error if a duplicate is detected.
if (!_store.Contains(transfer.Id))
{
_store.CreateItem(dataCopy, transfer.Id);
keyAndUpdatedVersion = _store.CreateItemFieldDictionary(transfer.Id);
}
else
{
recoverableErrorReportingContext.RecordConstraintError(_store.CreateItemFieldDictionary(transfer.Id));
keyAndUpdatedVersion = null;
}
commitKnowledgeAfterThisItem = false;
}
public override void UpdateItem(object itemData,
IEnumerable<SyncId> changeUnitsToUpdate,
ItemFieldDictionary keyAndExpectedVersion,
RecoverableErrorReportingContext recoverableErrorReportingContext,
out ItemFieldDictionary keyAndUpdatedVersion,
out bool commitKnowledgeAfterThisItem)
{
ItemTransfer transfer = (ItemTransfer)itemData;
ItemData dataCopy = new ItemData(transfer.ItemData);
IDictionary<uint, ItemField> expectedFields = (IDictionary<uint, ItemField>)keyAndExpectedVersion;
ulong idToUpdate = (ulong)expectedFields[CUSTOM_FIELD_ID].Value;
if (_store.Contains(idToUpdate))
{
ulong timeStamp = _store.UpdateItem(idToUpdate, dataCopy);
keyAndUpdatedVersion = _store.CreateItemFieldDictionary(transfer.Id);
}
else
{
// If the item to update does not exist, record an error on this change and
// continue with the rest of the session.
recoverableErrorReportingContext.RecordRecoverableErrorForChange(new RecoverableErrorData(new Exception("Item not found in the store")));
keyAndUpdatedVersion = null;
}
commitKnowledgeAfterThisItem = false;
}
public override void DeleteItem(ItemFieldDictionary keyAndExpectedVersion,
RecoverableErrorReportingContext recoverableErrorReportingContext,
out bool commitKnowledgeAfterThisItem)
{
IDictionary<uint, ItemField> expectedFields = (IDictionary<uint, ItemField>)keyAndExpectedVersion;
ulong id = (ulong)expectedFields[CUSTOM_FIELD_ID].Value;
if (_store.Contains(id))
{
_store.DeleteItem(id);
}
else
{
// If the item to delete does not exist, record an error on this change and
// continue with the rest of the session.
recoverableErrorReportingContext.RecordRecoverableErrorForChange(new RecoverableErrorData(new Exception("Item not found in the store")));
}
commitKnowledgeAfterThisItem = false;
}
Public Overrides Sub InsertItem(ByVal itemData As Object, ByVal changeUnitsToCreate As IEnumerable(Of SyncId), ByVal recoverableErrorReportingContext As RecoverableErrorReportingContext, ByRef keyAndUpdatedVersion As ItemFieldDictionary, ByRef commitKnowledgeAfterThisItem As Boolean)
Dim transfer As ItemTransfer = DirectCast(itemData, ItemTransfer)
Dim dataCopy As New ItemData(transfer.ItemData)
' Check for duplicates, and record a constraint error if a duplicate is detected.
If Not _store.Contains(transfer.Id) Then
_store.CreateItem(dataCopy, transfer.Id)
keyAndUpdatedVersion = _store.CreateItemFieldDictionary(transfer.Id)
Else
recoverableErrorReportingContext.RecordConstraintError(_store.CreateItemFieldDictionary(transfer.Id))
keyAndUpdatedVersion = Nothing
End If
commitKnowledgeAfterThisItem = False
End Sub
Public Overrides Sub UpdateItem(ByVal itemData As Object, ByVal changeUnitsToUpdate As IEnumerable(Of SyncId), ByVal keyAndExpectedVersion As ItemFieldDictionary, ByVal recoverableErrorReportingContext As RecoverableErrorReportingContext, ByRef keyAndUpdatedVersion As ItemFieldDictionary, ByRef commitKnowledgeAfterThisItem As Boolean)
Dim transfer As ItemTransfer = DirectCast(itemData, ItemTransfer)
Dim dataCopy As New ItemData(transfer.ItemData)
Dim expectedFields As IDictionary(Of UInteger, ItemField) = DirectCast(keyAndExpectedVersion, IDictionary(Of UInteger, ItemField))
Dim idToUpdate As ULong = CULng(expectedFields(CUSTOM_FIELD_ID).Value)
If _store.Contains(idToUpdate) Then
Dim timeStamp As ULong = _store.UpdateItem(idToUpdate, dataCopy)
keyAndUpdatedVersion = _store.CreateItemFieldDictionary(transfer.Id)
Else
' If the item to update does not exist, record an error on this change and
' continue with the rest of the session.
recoverableErrorReportingContext.RecordRecoverableErrorForChange(New RecoverableErrorData(New Exception("Item not found in the store")))
keyAndUpdatedVersion = Nothing
End If
commitKnowledgeAfterThisItem = False
End Sub
Public Overrides Sub DeleteItem(ByVal keyAndExpectedVersion As ItemFieldDictionary, ByVal recoverableErrorReportingContext As RecoverableErrorReportingContext, ByRef commitKnowledgeAfterThisItem As Boolean)
Dim expectedFields As IDictionary(Of UInteger, ItemField) = DirectCast(keyAndExpectedVersion, IDictionary(Of UInteger, ItemField))
Dim id As ULong = CULng(expectedFields(CUSTOM_FIELD_ID).Value)
If _store.Contains(id) Then
_store.DeleteItem(id)
Else
' If the item to delete does not exist, record an error on this change and
' continue with the rest of the session.
recoverableErrorReportingContext.RecordRecoverableErrorForChange(New RecoverableErrorData(New Exception("Item not found in the store")))
End If
commitKnowledgeAfterThisItem = False
End Sub
Sincronizando dois provedores
O seguinte exemplo de código mostra como réplicas de origem e de destino são sincronizadas. Depois de criar provedores de origem e de destino, o código define opções do SyncOrchestrator e sincroniza as réplicas.
Dim agent As New SyncOrchestrator()
agent.Direction = SyncDirectionOrder.DownloadAndUpload
agent.LocalProvider = providerA
agent.RemoteProvider = providerB
stats = agent.Synchronize()
SyncOrchestrator agent = new SyncOrchestrator();
agent.Direction = SyncDirectionOrder.DownloadAndUpload;
agent.LocalProvider = providerA;
agent.RemoteProvider = providerB;
stats = agent.Synchronize();
Manipulando conflitos
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. Para obter mais informações, consulte Resolvendo conflitos para provedores simples. O seguinte exemplo de código mostra os manipuladores de eventos que são especificados no construtor do 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
Filtrando dados
Alguns aplicativos exigem que os dados sejam filtrados para que somente os dados que atendam a critérios específicos sejam aplicados a um destino. Por exemplo, um vendedor pode exigir informações detalhadas somente dos produtos vendidos regularmente. Os provedores simples permitem que as réplicas filtrem dados por meio da implementação de uma interface de filtragem e da negociação do uso do filtro.
O seguinte exemplo de código usa interfaces de negociação de filtro para determinar se um filtro específico deve ser usado durante uma sessão de sincronização. A negociação de filtro permite que um provedor de destino especifique que o provedor de origem deve usar um ou mais filtros durante a enumeração de alterações; o provedor de origem pode aceitar ou rejeitar um filtro. Se um provedor de origem não der suporte a um dos filtros solicitados, o provedor de destino poderá optar por receber todos os dados e fazer a filtragem sozinho. O Sync Framework chama os provedores adequadamente para negociar o uso de filtro.
public bool RequestFilter
{
set
{
_requestFilter = value;
}
}
private bool _requestFilter = false;
void IRequestFilteredSync.SpecifyFilter(FilterRequestCallback filterRequest)
{
// Request a filter only if this provider represents a filtered replica.
if (_requestFilter)
{
if (!filterRequest("TheFilter", FilteringType.CurrentItemsOnly))
{
throw new SyncInvalidOperationException("Could not agree on filter.");
}
}
}
bool ISupportFilteredSync.TryAddFilter(object filter, FilteringType filteringType)
{
if (!((string)filter).Equals("TheFilter"))
{
throw new Exception("Filter is incorrect");
}
// Remember the filter.
_filter = (string)filter;
return true;
}
private string _filter = "";
Public WriteOnly Property RequestFilter() As Boolean
Set(ByVal value As Boolean)
_requestFilter = value
End Set
End Property
Private _requestFilter As Boolean = False
Private Sub SpecifyFilter(ByVal filterRequest As FilterRequestCallback) Implements IRequestFilteredSync.SpecifyFilter
' Request a filter only if this provider represents a filtered replica.
If _requestFilter Then
If Not filterRequest("TheFilter", FilteringType.CurrentItemsOnly) Then
Throw New SyncInvalidOperationException("Could not agree on filter.")
End If
End If
End Sub
Private Function TryAddFilter(ByVal filter As Object, ByVal filteringType As FilteringType) As Boolean Implements ISupportFilteredSync.TryAddFilter
If Not DirectCast(filter, String).Equals("TheFilter") Then
Throw New Exception("Filter is incorrect")
End If
' Remember the filter.
_filter = DirectCast(filter, String)
Return True
End Function
Private _filter As String = ""
O exemplo de código a seguir especifica primeiro uma opção de filtro None. Isso significa que os itens devem ser filtrados mesmo se forem conhecidos do destino. O exemplo de código implementa, em seguida, o método IsItemInFilterScope, que filtra itens com base em um dos valores de campo de item. Depois que o filtro é definido, o exemplo de código implementa o método UseFilterThisSession. Isso permite que um aplicativo especifique se a filtragem deve ser usada por sessão.
SimpleSyncProviderFilterOptions IFilteredSimpleSyncProvider.FilterOptions
{
get
{
return SimpleSyncProviderFilterOptions.None;
}
}
bool IFilteredSimpleSyncProvider.IsItemInFilterScope(ItemFieldDictionary KeyAndVersion)
{
ulong itemId = (ulong)KeyAndVersion[1].Value;
ItemData itemData = _store.Get(itemId);
if (itemData["data"] == "3333")
{
return false;
}
return true;
}
bool IFilteredSimpleSyncProvider.UseFilterThisSession
{
get
{
// Indicate whether a filter has been requested and agreed upon for this session.
return ("" != _filter);
}
}
Private ReadOnly Property FilterOptions() As SimpleSyncProviderFilterOptions Implements IFilteredSimpleSyncProvider.FilterOptions
Get
Return SimpleSyncProviderFilterOptions.None
End Get
End Property
Private Function IsItemInFilterScope(ByVal KeyAndVersion As ItemFieldDictionary) As Boolean Implements IFilteredSimpleSyncProvider.IsItemInFilterScope
Dim itemId As ULong = KeyAndVersion(1).Value
Dim data As ItemData = _store.Get(itemId)
If data("data") Is "3333" Then
Return False
End If
Return True
End Function
Private ReadOnly Property UseFilterThisSession() As Boolean Implements IFilteredSimpleSyncProvider.UseFilterThisSession
Get
' Indicate whether a filter has been requested and agreed upon for this session.
Return "" Is _filter
End Get
End Property
Executando exclusões somente no local
Alguns cenários de sincronização exigem a capacidade de excluir um item em uma réplica local sem propagar essa exclusão para outras réplicas. Por exemplo, um servidor pode sincronizar com vários dispositivos que armazenam informações para diferentes vendedores. Cada dispositivo tem espaço limitado. Assim, os vendedores excluem ordens concluídas antigas do dispositivo. Esses tipos de exclusões não devem ser propagadas para o servidor, porque o servidor ainda necessita dos dados. Os provedores simples permitem especificar que os dados só devem ser excluídos localmente. Para controlar o comportamento das exclusões por sessão, especifique a opção apropriada usando SetDeleteMode. O exemplo de código a seguir especifica que as exclusões não devem ser propagadas durante a sincronização.
public override void EnumerateItems(FullEnumerationContext context)
{
context.SetDeleteMode(SimpleSyncProviderDeleteMode.LocalOnly);
List<ItemFieldDictionary> items = new List<ItemFieldDictionary>();
foreach (ulong id in _store.Ids)
{
items.Add(_store.CreateItemFieldDictionary(id));
}
context.ReportItems(items);
}
public override void EnumerateItems(FullEnumerationContext context)
{
context.SetDeleteMode(SimpleSyncProviderDeleteMode.LocalOnly);
List<ItemFieldDictionary> items = new List<ItemFieldDictionary>();
foreach (ulong id in _store.Ids)
{
items.Add(_store.CreateItemFieldDictionary(id));
}
context.ReportItems(items);
}