Как создать управляемый простой поставщик
В этом разделе описаны важные компоненты образца приложения "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 имеет три свойства.
-
Пользовательские поля представляют собой поля в хранилище метаданных, идентифицируемые целыми числами. Если приложению требуется понятное имя для одного или нескольких полей, необходимо сопоставить это целое число с именем. Пользовательские поля можно определять в двух целях: для идентификации элементов и для предоставления информации о версиях этих элементов. Поля версий позволяют платформе Sync Framework определять, был ли изменен элемент или базовая единица. В данном примере поля версий содержат действительные данные из хранилища элементов, и для каждого поля в хранилище элементов имеется соответствующее поле. Такое соответствие «один к одному» необязательно, но эффективно. Более практичным решением было бы создание хэша из полей элементов и хранение его в одном пользовательском поле.
-
Правило идентификации определяет, какое пользовательское поле или группа полей используется для идентификации элемента. В данном случае используется поле 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);
}