Freigeben über


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:

  • CustomFields

    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.

  • IdentityRules

    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 und Phone eine Änderungseinheit sind und dass Guid eine andere Änderungseinheit ist. Für Guid könnten Sie die tatsächlichen Daten verwenden und für die andere Änderungseinheit einen Hash der Felder Zip und Phone 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