Как создать управляемый простой поставщик

В этом разделе описаны важные компоненты образца приложения "Sync101 using Simple Sync Provider", входящего в пакет Sync Framework SDK. Это приложение демонстрирует создание и синхронизацию простых поставщиков, основанных на точках синхронизации и полном перечислении. Образец приложения содержит три класса.

  • Объект MyFullEnumerationSimpleSyncProvider, производный от объекта FullEnumerationSimpleSyncProvider.

  • Объект MyAnchorEnumerationSimpleSyncProvider, производный от объекта AnchorEnumerationSimpleSyncProvider.

  • MySimpleDataStore служит хранилищем данных элементов в памяти. Класс MySimpleDataStore используется для этого образца и не входит в Sync Framework.

В классах обоих поставщиков реализуются следующие интерфейсы для обработки конфликтов: ISimpleSyncProviderConcurrencyConflictResolver и ISimpleSyncProviderConstraintConflictResolver.

В этом разделе описываются следующие компоненты приложения.

  • Создание хранилища метаданных

  • Идентификация элементов в хранилище элементов и хранилище метаданных

  • Перечисление элементов и загрузка данных

  • Синхронизация двух поставщиков

  • Обработка конфликтов

В разделе также кратко описана фильтрация данных и выполнение строго локальных удалений.

Создание хранилища метаданных

Для каждой реплики нужно хранилище метаданных. Для простых поставщиков хранилищем служит экземпляр класса SqlMetadataStore. В следующем примере кода параметры для хранилища указываются в конструкторе 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

В следующем примере кода создается хранилище.

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

В следующем примере кода это хранилище возвращается в свойстве поставщика.

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

Идентификация элементов в хранилище элементов и хранилище метаданных

Чтобы синхронизировать элемент, платформа Sync Framework должна иметь возможность идентифицировать его в хранилище элементов и сопоставить его идентификатор с внутренним идентификатором в хранилище метаданных. Необходимо также определить, не изменилась ли версия элемента со времени последнего сеанса синхронизации. Если версия изменилась и реплика назначения еще не содержит этой версии элемента, его необходимо синхронизировать. Если изменения синхронизируются на уровне базовой единицы, а не отдельного элемента, то платформа Sync Framework должна иметь возможность идентифицировать базовую единицу и ее версию. Базовая единица представляет собой изменение подэлемента, например поля телефонного номера в элементе, представляющем контакт. В этом образце базовые единицы не используются.

Задание формата идентификаторов хранилища метаданных

В следующем примере кода определяется конструктор для MyFullEnumerationSimpleSyncProvider и свойство IdFormats. Это позволяет среде выполнения платформы Sync Framework определить формат, в котором хранятся идентификаторы в хранилище метаданных. Если не используются гибкие идентификаторы, то платформа Sync Framework хранит идентификаторы реплик, элементов и базовых единиц в фиксированном формате. Если используются гибкие идентификаторы, для создания идентификаторов используются методы интерфейса 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

Задание полей элементов и схемы метаданных

Sync Framework сопоставляет данные хранилища элементов или дополнительно созданные метаданные с внутренними идентификаторами и версиями метаданных с помощью объекта ItemMetadataSchema, который доступен через свойство MetadataSchema. В следующих примерах кода показана подготовка входных данных для объекта ItemMetadataSchema. Константы в образце кода определяют целочисленные значения для каждого столбца в хранилище элементов. Эти значения используются при создании пользовательских определений полей и правил идентификации для объекта 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

Объект ItemMetadataSchema имеет три свойства.

  • CustomFields

    Пользовательские поля представляют собой поля в хранилище метаданных, идентифицируемые целыми числами. Если приложению требуется понятное имя для одного или нескольких полей, необходимо сопоставить это целое число с именем. Пользовательские поля можно определять в двух целях: для идентификации элементов и для предоставления информации о версиях этих элементов. Поля версий позволяют платформе Sync Framework определять, был ли изменен элемент или базовая единица. В данном примере поля версий содержат действительные данные из хранилища элементов, и для каждого поля в хранилище элементов имеется соответствующее поле. Такое соответствие «один к одному» необязательно, но эффективно. Более практичным решением было бы создание хэша из полей элементов и хранение его в одном пользовательском поле.

  • IdentityRules

    Правило идентификации определяет, какое пользовательское поле или группа полей используется для идентификации элемента. В данном случае используется поле CUSTOM_FIELD_ID (поле 0).

  • ChangeUnitVersionDefinitions (в этом образце не используется)

    Если используются базовые единицы, для них необходимо определить поля версий. При этом не требуется устанавливать сопоставление «один к одному» между базовыми единицами и информацией о версии или хранить актуальные данные. Базовые единицы могут охватывать сразу несколько полей. Например, приложение может указать, что поля Zip и Phone составляют одну базовую единицу, а поле Guid – другую. Для поля Guid можно использовать актуальные данные, а для второй базовой единицы — хэш на основе полей Zip и Phone или некоторый другой механизм определения версии.

Некоторые из методов, оперирующих данными хранилища элементов, такие как InsertItem, требуют на входе коллекцию объектов ItemField, представляющих каждое из полей. Объекты ItemFieldDictionary, используемые в качестве параметров таких методов, имеют значения индекса, совпадающие с индексами объектов CustomFieldDefinition.

Перечисление элементов и загрузка данных

Sync Framework должна иметь возможность перечислять элементы в исходном хранилище элементов, обнаруживать возможные изменения элементов или базовых единиц, а затем загружать измененные данные, чтобы их можно было применить в хранилище назначения. Обнаружение изменений обрабатывается средой выполнения Sync Framework, однако перечисление изменений и загрузка данных зависят от хранилища и выполняются для поставщиков полного перечисления путем реализации методов EnumerateItems и LoadChangeData. В следующем примере кода возвращается список элементов, перечисленных из объекта 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

В следующем примере кода возвращается объект, содержащий одно из изменений данных, перечисленное методом EnumerateItems. Платформа Sync Framework вызывает этот метод, пока не будут загружены все изменения.

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

Применение операций вставки, обновления и удаления

После того как Sync Framework обнаруживает и загружает изменения из источника, эти изменения и соответствующие изменения метаданных необходимо применить в конечной реплике. Изменения метаданных на стороне назначения обрабатываются платформой Sync Framework, однако применение изменений данных зависит от хранилища и выполняется реализацией следующих методов: DeleteItem, InsertItem и UpdateItem. В следующем примере кода приводится реализация каждого из этих методов.

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

Синхронизация двух поставщиков

В следующем примере кода показано, как синхронизируются исходная и конечная реплики. После создания поставщика источника и поставщика назначения в коде задаются параметры SyncOrchestrator и выполняется синхронизация реплик.

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();

Обработка конфликтов

В этом образце для обработки конфликтов параллелизма и конфликтов ограничений используются политики по умолчанию, определенные объектом ApplicationDefined. Это означает, что приложение зарегистрирует обработчики событий ItemConflicting и ItemConstraint, а также укажет действие по разрешению конфликтов, которые могут возникнуть в процессе синхронизации. Дополнительные сведения см. в разделе Обработка конфликтов для простых поставщиков. В следующем примере кода показаны обработчики событий, заданные в конструкторе MyFullEnumerationSimpleSyncProvider.

this.ItemConstraint += new EventHandler<SimpleSyncItemConstraintEventArgs>(OnItemConstraint);
this.ItemConflicting += new EventHandler<SimpleSyncItemConflictingEventArgs>(OnItemConflicting);
AddHandler Me.ItemConstraint, AddressOf HandleItemConstraint

В следующем примере кода демонстрируется обработчик события, определяющий действия по устранению конфликтов для события 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

В следующем примере кода показан метод MergeConstraintConflict, реализованный для разрешения конфликта ограничения слиянием.

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

Фильтрация данных

Для некоторых приложений требуется фильтровать данные, чтобы в назначении применялись только данные, соответствующие заданным условиям. Например, менеджеру по продажам могут требоваться подробные сведения только о тех продуктах, которые продаются регулярно. Простые поставщики поддерживают фильтрацию данных в репликах, реализуя интерфейс фильтрации и согласуя применение фильтров.

Следующий пример кода использует интерфейсы согласования фильтров для определения, должен ли использоваться определенный фильтр во время сеанса синхронизации. Согласование фильтров позволяет поставщику назначения указать, что поставщик источника должен использовать в процессе перечисления изменений один или несколько фильтров. Исходный поставщик может принять или отклонить этот фильтр. Если поставщик источника не поддерживает ни один из запрошенных фильтров, поставщик назначения может принять все данные и произвести фильтрацию самостоятельно. Sync Framework вызывает поставщиков соответственно для согласования использования фильтра.

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 = ""

В следующем примере кода вначале определяется параметр фильтрации None. Это означает, что элементы должны быть отфильтрованы, даже если уже известны в месте назначения. Затем в этом примере кода реализуется метод IsItemInFilterScope, который фильтрует элементы по значению одного из их полей. После определения фильтра в этом примере кода реализуется метод UseFilterThisSession. Это позволяет приложению указать, должна ли осуществляться фильтрация на уровне сеанса.

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

Выполнение строго локальных удалений

В некоторых сценариях синхронизации требуется возможность удалять элемент в локальной реплике, не распространяя это удаление на другие реплики. Например, сервер может синхронизироваться с несколькими устройствами, хранящими данные для различных продавцов. Объем места на каждом устройстве ограничен, и поэтому продавцы удаляют старые выполненные заказы. Такие операции удаления не должны передаваться на сервер, поскольку на сервере необходимо хранить эти данные. Простые поставщики позволяют указать, что данные нужно удалять только локально. Чтобы переключать режим удаления для каждого сеанса, укажите нужный параметр с помощью SetDeleteMode. В следующем примере кода показано, что операции удаления не должны распространяться во время синхронизации.

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); 
} 

См. также

Основные положения

Реализация простого пользовательского поставщика