HOW TO:建立 Managed 簡單提供者
本主題描述隨附於 Sync Framework SDK 之 "Sync101 using Simple Sync Provider"
範例應用程式的重要部分。這個應用程式會示範如何建立和同步處理以錨定為基礎和完整列舉的簡單提供者。範例應用程式有三個類別:
衍生自 FullEnumerationSimpleSyncProvider 的
MyFullEnumerationSimpleSyncProvider
。衍生自 AnchorEnumerationSimpleSyncProvider 的
MyAnchorEnumerationSimpleSyncProvider
。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 會將項目存放區資料或是您建立的其他中繼資料對應到內部中繼資料存放區識別碼和版本,其方式是使用 MetadataSchema 屬性所公開的 ItemMetadataSchema 物件。下列程式碼範例會提供 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 方法,以回應條件約束衝突的 Merge 解決動作:
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);
}