Vorgehensweise: Erstellen eines verwalteten einfachen Anbieters
In diesem Thema werden wichtige Teile der "Sync101 using Simple Sync Provider"
-Beispielanwendung beschrieben, die im Sync Framework-SDK enthalten ist. Diese Anwendung veranschaulicht das Erstellen und Synchronisieren von ankerbasierten einfachen Anbietern sowie solchen mit vollständiger Enumeration. Die Beispielanwendung verfügt über drei Klassen:
MyFullEnumerationSimpleSyncProvider
, die von FullEnumerationSimpleSyncProvider abgeleitet ist.MyAnchorEnumerationSimpleSyncProvider
, die von AnchorEnumerationSimpleSyncProvider abgeleitet ist.MySimpleDataStore
ist ein Elementdatenspeicher im Arbeitsspeicher.MySimpleDataStore
ist eine Klasse, die für dieses Beispiel verwendet wird. Diese ist nicht Teil von Sync Framework.
Beide Anbieterklassen implementieren die folgenden Schnittstellen zur Konfliktbehandlung: ISimpleSyncProviderConcurrencyConflictResolver und ISimpleSyncProviderConstraintConflictResolver.
In diesem Thema werden die folgenden Bereiche der Anwendung beschrieben:
Erstellen des Metadatenspeichers
Identifizieren von Elementen im Elementspeicher und Metadatenspeicher
Auflisten von Elementen und Laden von Daten
Synchronisieren von zwei Anbietern
Konfliktbehandlung
Im Thema wird ebenfalls kurz beschrieben, wie Daten gefiltert und lokale Löschvorgänge ausgeführt werden.
Erstellen des Metadatenspeichers
Jedes Replikat erfordert einen Metadatenspeicher. Bei einfachen Anbietern ist dies eine Instanz von SqlMetadataStore. Im folgenden Codebeispiel werden Optionen für einen Speicher im Konstruktor von MyFullEnumerationSimpleSyncProvider
angegeben:
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
Im folgenden Codebeispiel wird der Speicher erstellt:
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
Im folgenden Code wird dieser Speicher als Anbietereigenschaft zurückgegeben:
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
Identifizieren von Elementen im Elementspeicher und Metadatenspeicher
Damit ein Element synchronisiert werden kann, muss Sync Framework in der Lage sein, das Element im Elementspeicher zu identifizieren und diese Identität einer internen ID im Metadatenspeicher zuzuordnen. Außerdem muss ermittelt werden können, ob die Elementversion seit der letzten Synchronisierungssitzung geändert wurde. Wenn die Version geändert wurde und das Zielreplikat diese Version eines Elements nicht bereits enthält, sollte das Element synchronisiert werden. Wenn Änderungen auf der Ebene einer Änderungseinheit anstelle eines Elements synchronisiert werden, muss Sync Framework in der Lage sein, die Änderungseinheit und die zugehörige Version zu identifizieren. Eine Änderungseinheit entspricht einer Änderung eines Unterelements, wie zum Beispiel einem Feld für Telefonnummern in einem Element, das einen Kontakt darstellt. In diesem Beispiel werden keine Änderungseinheiten verwendet.
Festlegen des Formats von Metadatenspeicher-IDs
Im folgenden Codebeispiel werden der Konstruktor für MyFullEnumerationSimpleSyncProvider
und die IdFormats-Eigenschaft definiert. Dadurch kann die Sync Framework-Laufzeit ermitteln, welches Format der Metadatenspeicher für IDs verwendet. Wenn keine flexiblen IDs verwendet werden, verwendet Sync Framework zur Identifizierung von Replikaten, Elementen und Änderungseinheiten ein festes Format. Bei der Verwendung flexibler IDs werden diese mithilfe von ISimpleSyncProviderIdGenerator-Methoden generiert.
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
Festlegen von Elementfeldern und Metadatenschema
Sync Framework ordnet Elementspeicherdaten, oder zusätzliche von Ihnen erstellte Metadaten, internen Metadatenspeicher-IDs und Versionen zu. Dazu wird ein ItemMetadataSchema-Objekt verwendet, das von der MetadataSchema-Eigenschaft bereitgestellt wird. In den folgenden Codebeispielen wird die Eingabe für das ItemMetadataSchema-Objekt bereitgestellt. Die Konstanten im Beispielcode definieren für jede Spalte im Elementspeicher einen ganzzahligen Wert. Diese Werte werden beim Erstellen der benutzerdefinierten Felddefinitionen und Identitätsregeln für das ItemMetadataSchema-Objekt verwendet.
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
Vom ItemMetadataSchema-Objekt werden drei Eigenschaften bereitgestellt:
-
Benutzerdefinierte Felder sind Felder im Metadatenspeicher, die durch Ganzzahlen identifiziert werden. Wenn eine Anwendung einen Anzeigenamen für ein oder mehrere Felder erfordert, sollte die ganze Zahl einem Namen zugeordnet werden. Benutzerdefinierte Felder werden aus zwei Gründen definiert: um Elemente zu identifizieren und um Versionsinformationen zu diesen Elementen bereitzustellen. Anhand von Versionsfeldern kann Sync Framework bestimmen, ob ein Element oder eine Änderungseinheit geändert wurde. In diesem Beispiel enthalten die Versionsfelder die tatsächlichen Daten aus dem Elementspeicher. Somit gibt es ein Feld für jedes Feld im Elementspeicher. Diese 1:1-Entsprechung ist weder erforderlich noch effizient. Eine praktischere Lösung wäre, einen Hash der Elementfelder in einem einzigen benutzerdefinierten Feld zu speichern.
-
Die Identitätsregel gibt an, welche benutzerdefinierten Felder zur Identifizierung eines Elements verwendet werden sollen. In diesem Fall wird das
CUSTOM_FIELD_ID
-Feld (Feld 0) verwendet. ChangeUnitVersionDefinitions (wird in diesem Beispiel nicht verwendet)
Wenn Änderungseinheiten verwendet werden, müssen Versionsfelder für die Änderungseinheiten definiert werden. Es besteht keine Anforderung für eine 1:1-Zuordnung zwischen Änderungseinheiten und Versionsinformationen oder dafür, dass die tatsächlichen Daten gespeichert werden. Änderungseinheiten können auch mehrere Felder umfassen. Diese Anwendung könnte z. B. angeben, dass
Zip
undPhone
eine Änderungseinheit sind und dassGuid
eine andere Änderungseinheit ist. FürGuid
könnten Sie die tatsächlichen Daten verwenden und für die andere Änderungseinheit einen Hash der FelderZip
undPhone
oder einen anderen Mechanismus zum Bestimmen der Version.
Einige der Methoden, die mit Elementspeicherdaten arbeiten, wie z. B. InsertItem, erfordern eine Auflistung von ItemField-Objekten, die die einzelnen Felder darstellen. Die ItemFieldDictionary-Objekte, die Parameter für diese Methoden sind, verfügen über die gleichen Indexwerte wie die CustomFieldDefinition-Objekte.
Auflisten von Elementen und Laden von Daten
Sync Framework muss in der Lage sein, Elemente im Quellelementspeicher aufzulisten und zu erkennen, ob Elemente oder Änderungseinheiten geändert wurden. Anschließend müssen die geänderten Daten geladen werden, sodass diese für den Zielspeicher übernommen werden können. Die Änderungserkennung wird von der Sync Framework-Laufzeit behandelt. Dagegen sind die Änderungsenumeration und das Laden der Daten speicherspezifisch und werden bei Anbietern mit vollständiger Enumeration durch Implementieren von EnumerateItems und LoadChangeData behandelt. Im folgenden Codebeispiel wird eine Liste von Elementen zurückgegeben, die vom MySimpleDataStore
-Objekt aufgelistet werden:
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
Im folgenden Codebeispiel wird ein Objekt zurückgegeben, das eine der Datenänderungen enthält, die von EnumerateItems aufgelistet wurde. Sync Framework ruft diese Methode auf, bis alle Änderungen geladen wurden.
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
Anwenden von Einfügungen, Aktualisierungen und Löschungen
Nachdem Sync Framework Änderungen der Quelle erkannt und geladen hat, müssen diese Änderungen sowie entsprechende Metadatenänderungen für das Zielreplikat übernommen werden. Metadatenänderungen am Ziel werden von Sync Framework behandelt. Dagegen ist das Übernehmen von Datenänderungen speicherspezifisch und wird durch Implementieren der folgenden Methoden behandelt: DeleteItem, InsertItem und UpdateItem. In den folgenden Codebeispielen wird eine Implementierung für jede dieser Methoden bereitgestellt:
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
Synchronisieren von zwei Anbietern
Im folgenden Codebeispiel wird gezeigt, wie Quell- und Zielreplikate synchronisiert werden. Nach dem Erstellen von Quellen- und Zielanbietern, werden die SyncOrchestrator-Optionen vom Code festgelegt und die Replikate synchronisiert.
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();
Konfliktbehandlung
In diesem Beispiel werden die Konfliktbehandlungsrichtlinien für Parallelitätskonflikte und Einschränkungskonflikte wie bei der Standardeinstellung von ApplicationDefined
beibehalten. Dies bedeutet, dass die Anwendung für das ItemConflicting-Ereignis und ItemConstraint-Ereignis registriert wird und eine Aktion zur Auflösung von Konflikten festgelegt wird, die bei der Synchronisierungsverarbeitung auftreten. Weitere Informationen finden Sie unter Konfliktbehandlung für einfache Anbieter. Im folgenden Codebeispiel werden die Ereignishandler veranschaulicht, die im Konstruktor von MyFullEnumerationSimpleSyncProvider
angegeben werden:
this.ItemConstraint += new EventHandler<SimpleSyncItemConstraintEventArgs>(OnItemConstraint);
this.ItemConflicting += new EventHandler<SimpleSyncItemConflictingEventArgs>(OnItemConflicting);
AddHandler Me.ItemConstraint, AddressOf HandleItemConstraint
Im folgenden Codebeispiel werden die Ereignishandler veranschaulicht, die die Konfliktauflösungsaktionen auf Merge
festlegen:
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
Im folgenden Codebeispiel wird die MergeConstraintConflict-Methode veranschaulicht, die als Antwort auf eine Auflösungsaktion von "Merge" für einen Einschränkungskonflikt implementiert wird:
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
Filtern von Daten
Einige Anwendungen erfordern das Filtern von Daten, sodass nur Daten auf ein Ziel angewendet werden, die bestimmte Kriterien erfüllen. Beispielsweise könnte eine Vertriebsmitarbeiterin ausführliche Produktinformationen nur für solche Produkte benötigen, die sie regelmäßig verkauft. Mithilfe von einfachen Anbietern können Replikate Daten filtern, indem eine Filterschnittstelle implementiert und die Filterverwendung ausgehandelt wird.
Im folgenden Codebeispiel werden Schnittstellen zur Filteraushandlung verwendet, um festzulegen, ob ein bestimmter Filter während einer Synchronisierungssitzung verwendet werden soll. Durch Filteraushandlung kann ein Zielanbieter festlegen, dass der Quellenanbieter während der Änderungsenumeration einen oder mehrere Filter verwenden soll. Der Quellenanbieter kann einen Filter zulassen oder ablehnen. Wenn ein Quellenanbieter irgendeinen der angeforderten Filter nicht unterstützt, kann der Zielanbieter entscheiden, alle Daten zu empfangen und die Filterung selbst durchzuführen. Sync Framework ruft die Anbieter zum Aushandeln der Filterverwendung entsprechend auf.
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 = ""
Im folgenden Codebeispiel wird zuerst eine Filteroption von None angegeben. Dies bedeutet, dass Elemente auch dann herausgefiltert werden sollen, wenn sie dem Ziel bereits bekannt sind. Im Codebeispiel wird dann die IsItemInFilterScope-Methode implementiert, die Elemente anhand eines der Elementfeldwerte herausfiltert. Nachdem der Filter definiert wurde, wird im Codebeispiel die UseFilterThisSession-Methode implementiert. Dadurch kann von einer Anwendung festgelegt werden, ob eine Filterung für die jeweilige Sitzung verwendet werden soll.
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
Ausführen von lokalen Löschungen
Einige Synchronisierungsszenarien erfordern die Möglichkeit, ein Element auf einem lokalen Replikat zu löschen, ohne diese Löschung an andere Replikate weiterzugeben. Beispielsweise könnte ein Server mit mehreren Geräten synchronisiert werden, auf denen Informationen für verschiedene Vertriebsmitarbeiter gespeichert werden. Jedes Gerät verfügt nur über beschränkten Speicherplatz, sodass die Vertriebsmitarbeiter alte, abgeschlossene Aufträge auf dem Gerät löschen. Diese Arten von Löschungen sollen nicht zum Server weitergegeben werden, da die entsprechenden Daten dort weiterhin benötigt werden. Bei einfachen Anbietern können Sie angeben, dass die Daten nur lokal gelöscht werden sollen. Um das Verhalten von Löschungen für die jeweilige Sitzung zu steuern, geben Sie die entsprechende Option mit SetDeleteMode an. Im folgenden Codebeispiel wird festgelegt, dass Löschungen während der Synchronisierung nicht weitergegeben werden sollen.
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);
}
Siehe auch
Konzepte
Implementieren eines benutzerdefinierten einfachen Anbieters