为简单提供程序处理冲突

如果可能,应对应用程序进行设计以避免发生冲突。冲突检测和解决会导致复杂性、处理过程和网络流量的增加。在某些应用程序中,冲突是不可避免的。例如,在销售应用程序中,两个销售人员可能共享一个区域。他们都能更新同一客户和订单的数据。为了确保对同步社区中的项进行的变更被正确传播,目标提供程序必须检测并处理从源提供程序发送的项和目标副本中的项之间发生的冲突。Sync Framework 提供执行检测和处理冲突所需的大多数工作的对象。

Sync Framework 检测项级别或变更单位级别的冲突。Sync Framework 识别在同步期间可能发生的两种类别的冲突:“并发冲突”和“约束冲突”。当同一项或变更单位在稍后将进行同步的两个不同副本上发生变更时,发生并发冲突。约束冲突指违反有关项或变更单元的约束(如文件夹的关系或文件系统中同名数据的位置)的冲突。Sync Framework 将约束冲突划分为以下三种类型。

  • “抵触冲突”**:当某一项与目标存储区中的另一项发生冲突而无法保存该项时(例如,当源提供程序发送的文件与目标副本中已存在的文件具有相同的名称和位置时),发生此类冲突。

  • “缺少父项冲突”**:当某个项所需的父项不存在而无法在分层数据存储区中保存该项时(例如,当源提供程序发送一个文件以保存在某个目录中,但该目录在目标副本中不存在时),将发生此类冲突。

  • 其他约束冲突:当要保存的项违反了目标副本的约束时(例如,源提供程序发送的文件过大而无法保存在目标副本上,或者变更违反了目标副本上的某些业务逻辑),发生此类冲突。

约束与项存储区的特定性能相关,例如在数据库中十分常见的外键约束。简单提供程序只支持抵触约束冲突。有关标准自定义提供程序的冲突处理的更多信息,请参见检测和解决约束冲突

理解冲突处理

要决定如何处理并发冲突和约束冲突,您必须回答两个重要的问题:

  • 是应该在同步过程中自动解决冲突,还是应该在检测到冲突时通知应用程序,以便该应用程序可以驱动冲突解决?

  • 是应该通过指定源或目标胜出而解决所有冲突,还是要求更复杂的冲突处理?例如,在并发冲突中,您可能要将源数据和目标数据合并到应用于这两个副本的单个项中。

在回答这些问题后,您可以指定 Sync Framework 在遇到冲突时应如何反应:

  1. 为并发冲突和抵触约束冲突指定解决策略。该策略确定 Sync Framework 是否自动解决该冲突或者应用程序是否默认为响应某一事件以处理该冲突。

    如果您指定并非默认设置的其他策略,则 Sync Framework 会设置发生冲突时的适当的冲突解决操作。例如,如果您为并发冲突指定“源胜出”策略,则在同步会话过程中检测到此类型的冲突时,将设置“源胜出”操作。如果您接受冲突解决策略之一或全部两个策略的默认设置,则提供程序或应用程序必须响应在检测到冲突时激发的事件。提供程序可通过实现以下方法进行响应:

    如果提供程序未实现这些方法,则使用以下应用程序回调,以便应用程序可以设置冲突解决操作。如果应用程序未响应这些事件,则冲突解决会延迟到后续的同步会话之时。在此情况下,该冲突将永远不会解决,除非冲突数据或应用程序发生变更。

    为响应冲突,提供程序或应用程序必须设置某一冲突解决操作。

    除了设置冲突解决操作外,您还可以在事件处理程序中包括自定义代码。例如,您可以在处理冲突项时在用户界面中显示这些冲突项。

  2. 对于 Sync Framework 或应用程序设置的某些冲突解决操作,您必须实现以下接口中的一个或全部两个:

    对于并发冲突,您为这些接口实现的冲突解决方法按响应的冲突类型(例如更新-更新冲突)区分。对于约束冲突,您实现的冲突解决方法按解决结果(例如重命名源项)区分。

    对于并发冲突,如果操作设置为 Merge(对于托管代码)或 SRA_MERGE(对于非托管代码),则必须实现处理三种并发冲突的以下方法:

    实现应采用适合于副本和应用程序的方法来合并冲突项,只要有一个代表两个冲突项的最终项。

    对于抵触约束冲突,实现基于可设置的操作的方法:

托管代码示例

在这个示例中,用于并发冲突和约束冲突的冲突解决策略作为 ApplicationDefined 的默认设置保留。这意味着应用程序将注册 ItemConflictingItemConstraint 事件,并且指定在同步处理期间发生冲突时用于解决冲突的操作。下面的代码示例显示在 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

请参阅

概念

实现简单自定义提供程序
如何创建托管的简单提供程序