處理簡單提供者的衝突
如果可能的話,請設計應用程式來避免發生衝突。衝突偵測和解決會導入額外的複雜性、處理和網路流量。在某些應用程式中,衝突是無法避免的。例如,在銷售人力應用程式中,兩名銷售人員可能會有共同的區域。這兩名銷售人員都可以更新相同客戶和訂單的資料。為了確保同步處理社群中的項目變更能夠正確傳播,目的地提供者必須偵測及處理從來源提供者傳送之項目以及目的地複寫中的項目之間所發生的衝突。Sync Framework 會提供可執行偵測及處理衝突所需之大部分工作的物件。
Sync Framework 會偵測項目或變更單位層級的衝突。Sync Framework 會辨識可能在同步處理期間發生之兩種類別的衝突:「並行衝突」(Concurrency Conflict) 和「條件約束衝突」(Constraint Conflict)。在兩個不同的複寫上變更了相同的項目或變更單位,然後進行同步處理時,就會發生並行衝突。條件約束衝突是違反在項目或變更單位上所設條件約束 (例如資料夾的關聯性或檔案系統之中名稱完全相同之資料的位置) 的衝突。Sync Framework 將條件約束衝突分成下列三種。
「衝突」(Collision Conflict) 發生於因為某個項目與目的地存放區中的其他項目衝突而無法儲存該項目時,例如當來源提供者所傳送的檔案與已經存在目的地複寫中的檔案具有相同名稱和位置時。
「遺失父系衝突」(Missing Parent Conflict) 發生於因為某個項目需要的父項目不存在,而無法將此項目儲存在階層式資料存放區時,例如當來源提供者傳送的檔案要儲存於目的地複寫中所不存在的目錄時。
其他條件約束衝突發生於要儲存的項目違反目的地複寫的條件約束時,例如來源提供者傳送的檔案太大,而無法儲存在目的地複寫上,或是此變更違反目的地複寫上的某個商務邏輯時。
條件約束與項目存放區的特定功能有關,例如資料庫中常用的外部索引鍵條件約束。簡單提供者只支援衝突 (Collision) 條件約束的衝突 (Conflict)。如需標準自訂提供者之衝突處理的詳細資訊,請參閱偵測及解決條件約束衝突。
了解衝突處理
若要決定如何處理並行衝突和條件約束衝突,您必須回答兩個重要的問題:
衝突是應該在同步處理期間自動解決,還是應該在偵測到衝突時通知應用程式,讓應用程式可以驅動衝突解決方案?
應該指定來源或目的地的哪一方獲勝來解決所有衝突,還是需要更複雜的方式來處理衝突?例如,在並行衝突中,您可能會想要將來源和目的地資料合併到適用於兩個複寫的單一項目中。
在回答這些問題之後,您可以指定 Sync Framework 在遇到衝突時應該有的行為模式為何:
針對並行衝突和衝突 (Collision) 條件約束的衝突 (Conflict) 指定解決原則。此原則會決定 Sync Framework 是否會自動解決衝突,或者應用程式是否會預設為回應某個事件來處理衝突。
Managed 程式碼:此提供者會指定 Configuration 屬性所公開之 KnowledgeSyncProviderConfiguration 物件的 ConflictResolutionPolicy 和 CollisionConflictResolutionPolicy 列舉中的值。
Unmanaged 程式碼:當應用程式啟動同步處理工作階段時,CONFLICT_RESOLUTION_POLICY 會傳遞到 ISyncSession::Start。在這一版中,您不能傳遞 Unmanaged 程式碼的 COLLISION_CONFLICT_RESOLUTION_POLICY。
如果您指定預設值以外的原則,Sync Framework 會在發生衝突時設定適當的衝突解決動作。例如,如果您為並行衝突指定「來源方獲勝」的原則,當在同步處理工作階段期間偵測到該類型的衝突時,便會設定「來源方獲勝」動作。如果您接受一個或兩個解決原則的預設值,提供者或應用程式必須在偵測到衝突時回應所引發的事件。此提供者可以實作下列方法來回應:
Managed 程式碼:OnItemConflicting 和 OnItemConstraint
Unmanaged 程式碼:ISimpleSyncEvents::OnConcurrencyConflict 和 ISimpleSyncEvents::OnConstraintConflict。
如果此提供者無法實作這些方法,便會使用下列應用程式回呼,好讓應用程式可以設定解決動作。如果應用程式不回應這些事件,將會延遲衝突的解決方案,直到後續的同步處理工作階段為止。在此情況下,除非衝突的資料或應用程式變更,否則衝突永遠不會解決。
Managed 程式碼:ItemConflicting 和 ItemConstraint
Unmanaged 程式碼:ISyncCallback::OnConflict 和 ISyncConstraintCallback::OnConstraintConflict。
在回應衝突時,此提供者或應用程式必須設定解決動作。
Managed 程式碼:呼叫 SetResolutionAction 並傳遞 ConflictResolutionAction 中的值,或呼叫 SetResolutionAction 並傳遞 ConstraintConflictResolutionAction 中的值。
Unmanaged 程式碼:呼叫 IChangeConflict::SetResolveActionForChange 或 IChangeConflict::SetResolveActionForChangeUnit 並傳遞 SYNC_RESOLVE_ACTION 中的值,或呼叫 IConstraintConflict::SetConstraintResolveActionForChange 或 IConstraintConflict::GetConstraintResolveActionForChangeUnit 並傳遞 SYNC_CONSTRAINT_RESOLVE_ACTION 中的值。
除了設定解決動作以外,您也可以在事件處理常式中包含自訂程式碼。例如,您可以在處理衝突項目時,於使用者介面中顯示這些項目。
如果是 Sync Framework 或應用程式設定的某些解決動作,您必須實作下列其中一個或兩個介面:
Managed 程式碼:ISimpleSyncProviderConcurrencyConflictResolver 和 ISimpleSyncProviderConstraintConflictResolver。
Unmanaged 程式碼:ISimpleSyncProviderConcurrencyConflictResolver 和 ISimpleSyncProviderConstraintConflictResolver。
如果是並行衝突,您為這些介面所實作的解決方法會依照其回應的衝突類型來區分,例如更新對更新的衝突。如果是條件約束衝突,您所實作的解決方法會依照解決方案的結果來區分,例如重新命名來源項目。
對於並行衝突而言,如果動作設定為 Merge (Managed 程式碼) 或 SRA_MERGE (Unmanaged 程式碼),您必須實作下列方法來處理三種類型的並行衝突:
Managed 程式碼:ResolveUpdateUpdateConflict、ResolveLocalDeleteRemoteUpdateConflict 和 ResolveLocalUpdateRemoteDeleteConflict。
Unmanaged 程式碼:ISimpleSyncProviderConcurrencyConflictResolver::ResolveUpdateUpdateConflict、ISimpleSyncProviderConcurrencyConflictResolver::ResolveLocalDeleteRemoteUpdateConflict 和 ISimpleSyncProviderConcurrencyConflictResolver::ResolveLocalUpdateRemoteDeleteConflict。
實作應該使用適合複寫和應用程式的方式來合併衝突的項目,只要有一個最終項目代表兩個衝突的項目即可。
如果是衝突 (Collision) 條件約束的衝突 (Conflict),請根據可以設定的動作來實作方法:
Managed 程式碼 (ConstraintConflictResolutionAction):
Unmanaged 程式碼 (SYNC_CONSTRAINT_RESOLVE_ACTION):
Managed 程式碼範例
在此範例中,並行衝突和條件約束衝突的衝突處理原則會保留為 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