Procedura: creare un provider semplice gestito
In questo argomento vengono descritte parti importanti dell'applicazione di esempio "Sync101 using Simple Sync Provider"
inclusa in Sync Framework SDK. Tramite questa applicazione viene illustrato come creare e sincronizzare provider semplici basati su ancoraggio e con enumerazione completa. L'applicazione di esempio presenta tre classi:
MyFullEnumerationSimpleSyncProvider
che deriva da FullEnumerationSimpleSyncProvider.MyAnchorEnumerationSimpleSyncProvider
che deriva da AnchorEnumerationSimpleSyncProvider.MySimpleDataStore
è un archivio dati di elementi in memoria.MySimpleDataStore
è una classe utilizzata per questo esempio che non fa parte di Sync Framework.
In entrambe le classi del provider vengono implementate le interfacce di gestione dei conflitti seguenti: ISimpleSyncProviderConcurrencyConflictResolver e ISimpleSyncProviderConstraintConflictResolver.
In questo argomento vengono descritte le parti seguenti dell'applicazione:
Creazione dell'archivio dei metadati
Identificazione degli elementi nell'archivio di elementi e nell'archivio dei metadati
Enumerazione di elementi e caricamento di dati
Sincronizzazione di due provider
Gestione dei conflitti
Nell'argomento viene anche brevemente descritto come filtrare i dati ed eseguire eliminazioni solo locali.
Creazione dell'archivio dei metadati
Ogni replica richiede un archivio dei metadati, che per i provider semplici è un'istanza di SqlMetadataStore. Nell'esempio di codice seguente vengono specificate le opzioni per un archivio nel costruttore di 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
Nell'esempio di codice seguente viene creato l'archivio:
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
Nel codice seguente viene restituito tale archivio come una proprietà del provider:
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
Identificazione di elementi nell'archivio di elementi e nell'archivio dei metadati
Per sincronizzare un elemento, Sync Framework deve essere in grado di identificare l'elemento nell'archivio di elementi e di eseguire il mapping di tale identità a un ID interno nell'archivio dei metadati. Deve inoltre essere in grado di determinare se la versione dell'elemento è cambiata dall'ultima sessione di sincronizzazione. Se la versione è cambiata e la replica di destinazione non contiene già tale versione dell'elemento, quest'ultimo deve essere sincronizzato. Se le modifiche vengono sincronizzate al livello di un'unità di modifica anziché di un elemento, Sync Framework deve essere in grado di identificare l'unità di modifica e la relativa versione. Un'unità di modifica rappresenta una modifica di un elemento secondario, ad esempio il campo numero di telefono in un elemento che rappresenta un contatto. In questo esempio non vengono utilizzate unità di modifica.
Definizione del formato degli ID dell'archivio dei metadati
Nell'esempio di codice seguente viene definito il costruttore per MyFullEnumerationSimpleSyncProvider
e la proprietà IdFormats. In questo modo il runtime di Sync Framework può determinare il formato utilizzato dall'archivio dei metadati per gli ID. Se non vengono utilizzati ID flessibili, in Sync Framework viene utilizzato un formato fisso per identificare repliche, elementi e unità di modifica. Se vengono utilizzati ID flessibili, per generare gli ID vengono utilizzati i metodi ISimpleSyncProviderIdGenerator.
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
Definizione dei campi elemento e dello schema dei metadati
Sync Framework esegue il mapping dei dati dell'archivio di elementi, o di metadati aggiuntivi creati, a versioni e ID dell'archivio dei metadati interni tramite un oggetto ItemMetadataSchema esposto dalla proprietà MetadataSchema. Negli esempi di codice seguenti viene fornito l'input per l'oggetto ItemMetadataSchema. Le costanti nel codice di esempio definiscono un valore integer per ogni colonna nell'archivio di elementi. Questi valori vengono utilizzati nella creazione di definizioni di campo e regole di identità personalizzate per l'oggetto 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
L'oggetto ItemMetadataSchema espone tre proprietà:
-
I campi personalizzati sono campi dell'archivio dei metadati identificati da valori integer. Se un'applicazione richiede un nome descrittivo per uno o più campi, il valore intero deve essere mappato a un nome. I campi personalizzati vengono definiti per due ragioni: identificare gli elementi e fornire informazioni sulla versione per tali elementi. I campi versione consentono a Sync Framework di determinare se un elemento o un'unità di modifica è cambiata. In questo esempio i campi versione contengono i dati effettivi dell'archivio di elementi, pertanto è presente un campo per ogni campo dell'archivio di elementi. Questa corrispondenza uno-a-uno non è necessaria né efficace. Una soluzione più pratica consiste nell'utilizzare un hash dei campi elemento e archiviarlo in un solo campo personalizzato.
-
La regola di identità specifica il campo o i campi personalizzati che devono essere utilizzati per identificare un elemento. In questo caso, viene utilizzato il campo
CUSTOM_FIELD_ID
(campo 0). ChangeUnitVersionDefinitions (non utilizzata in questo esempio)
Se vengono utilizzate unità di modifica, è necessario definire campi versione per le unità di modifica. Nessun requisito prevede l'esistenza di un mapping uno-a-uno tra unità di modifica e informazioni sulla versione né impone l'archiviazione dei dati effettivi. Le unità di modifica possono estendersi su più campi. Ad esempio, in questa applicazione potrebbe essere specificato che
Zip
ePhone
sono un'unità di modifica e cheGuid
è un'altra unità di modifica. PerGuid
, è possibile utilizzare i dati effettivi e per l'altra unità di modifica un hash dei campiZip
ePhone
o un altro meccanismo per determinare la versione.
Alcuni dei metodi applicabili ai dati dell'archivio di elementi, ad esempio InsertItem, richiedono una raccolta di oggetti ItemField che rappresentano ciascun campo. Gli oggetti ItemFieldDictionary che sono parametri di questi metodi dispongono degli stessi valori di indice di quelli specificati negli oggetti CustomFieldDefinition.
Enumerazione di elementi e caricamento di dati
Sync Framework deve essere in grado di enumerare elementi nell'archivio di elementi di origine, di rilevare se unità di modifica o elementi sono cambiati e infine di caricare i dati modificati in modo che possano essere applicati all'archivio di destinazione. Il rilevamento delle modifiche viene gestito dal runtime di Sync Framework, ma l'enumerazione delle modifiche e il caricamento dei dati sono attività specifiche dell'archivio e vengono gestite per i provider di enumerazione completa implementando EnumerateItems e LoadChangeData. Nell'esempio di codice seguente viene restituito un elenco di elementi enumerati dall'oggetto 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
Nell'esempio di codice seguente viene restituito un oggetto che contiene una delle modifiche ai dati enumerate da EnumerateItems. Sync Framework chiama questo metodo finché non sono state caricate tutte le modifiche.
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
Applicazione di inserimenti, aggiornamenti ed eliminazioni
Dopo che in Sync Framework sono state rilevate e caricate le modifiche dalla replica di origine, tali modifiche e le modifiche ai metadati corrispondenti devono essere applicate alla replica di destinazione. Le modifiche ai metadati nella destinazione vengono gestite da Sync Framework, ma l'applicazione delle modifiche ai dati è un'attività specifica dell'archivio e viene gestita implementando i metodi seguenti: DeleteItem, InsertItem e UpdateItem. Negli esempi di codice seguenti viene fornita un'implementazione per ognuno di questi metodi:
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
Sincronizzazione di due provider
Nell'esempio di codice seguente viene illustrato come vengono sincronizzate le repliche di origine e di destinazione. Dopo avere creato i provider di origine e di destinazione, nel codice vengono impostate le opzioni di SyncOrchestrator e vengono sincronizzate le repliche.
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();
Gestione dei conflitti
In questo esempio, i criteri di gestione dei conflitti per conflitti di concorrenza e di vincoli corrispondono all'impostazione predefinita di ApplicationDefined
. Ciò significa che l'applicazione verrà registrata per gli eventi ItemConflicting e ItemConstraint e specificherà un'azione per risolvere i conflitti che si dovessero eventualmente verificare durante l'elaborazione della sincronizzazione. Per ulteriori informazioni, vedere Gestione dei conflitti per i provider semplici. Nell'esempio di codice seguente vengono illustrati i gestori eventi specificati nel costruttore di MyFullEnumerationSimpleSyncProvider
:
this.ItemConstraint += new EventHandler<SimpleSyncItemConstraintEventArgs>(OnItemConstraint);
this.ItemConflicting += new EventHandler<SimpleSyncItemConflictingEventArgs>(OnItemConflicting);
AddHandler Me.ItemConstraint, AddressOf HandleItemConstraint
Nell'esempio di codice seguente vengono illustrati i gestori eventi che impostano le azioni di risoluzione dei conflitti su 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
Nell'esempio di codice seguente viene illustrato il metodo MergeConstraintConflict implementato per rispondere a un'azione di risoluzione di tipo unione per un conflitto di vincoli:
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
Filtro dei dati
Per alcune applicazioni è necessario che i dati vengano filtrati, in modo che solo i dati che soddisfano criteri specifici vengano applicati a una destinazione. Ad esempio, un venditore potrebbe richiedere informazioni dettagliate solo per i prodotti che vende regolarmente. I provider semplici consentono alle repliche di filtrare i dati tramite l'implementazione di un'interfaccia di filtro e la negoziazione dell'utilizzo del filtro.
Nell'esempio di codice seguente vengono utilizzate interfacce di negoziazione del filtro per stabilire se un filtro specifico deve essere utilizzato durante una sessione di sincronizzazione. La negoziazione del filtro consente a un provider di destinazione di specificare che il provider di origine deve utilizzare uno o più filtri durante l'enumerazione delle modifiche. Il provider di origine può accettare o rifiutare un filtro. Se un provider di origine non supporta alcuno dei filtri richiesti, il provider di destinazione può scegliere di ricevere tutti i dati ed eseguire autonomamente il filtro. Sync Framework chiama di conseguenza i provider appropriati per negoziare l'utilizzo del 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 = ""
Nell'esempio di codice seguente viene innanzitutto specificata un'opzione di filtro di None. Ciò significa che gli elementi devono essere filtrati anche se sono già noti alla destinazione. Nell'esempio di codice viene quindi implementato il metodo IsItemInFilterScope che filtra gli elementi in base a uno dei valori di campi di elementi. Dopo avere definito il filtro, nell'esempio di codice viene implementato il metodo UseFilterThisSession. Ciò consente a un'applicazione di specificare se il filtro deve essere utilizzato in base a ogni singola sessione.
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
Esecuzione di eliminazioni solo locali
In alcuni scenari di sincronizzazione è richiesta la possibilità di eliminare un elemento in una replica locale senza propagare tale eliminazione ad altre repliche. Ad esempio, un server potrebbe essere sincronizzato con diversi dispositivi in cui sono archiviate informazioni per venditori diversi. Ogni dispositivo dispone di uno spazio limitato, pertanto i venditori eliminano i precedenti ordini completati dal dispositivo. Questi tipi di eliminazioni non devono essere propagati al server, dove tali dati sono ancora necessari. I provider semplici consentono di specificare che i dati devono essere eliminati solo localmente. Per controllare il comportamento delle eliminazioni per ogni singola sessione, specificare l'opzione appropriata tramite SetDeleteMode. Nell'esempio di codice seguente viene specificato che le eliminazioni non devono essere propagate durante la sincronizzazione.
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);
}