Share via


Handling Conflicts for Simple Providers

If it is possible, design applications to avoid conflicts. Conflict detection and resolution introduce additional complexity, processing, and network traffic. In some applications, conflicts cannot be avoided. For example, in a sales force application, two salespeople might share a territory. Both salespeople could update the data for the same customer and orders. To ensure that changes made to items in the synchronization community are correctly propagated, the destination provider must detect and handle conflicts that occur between items sent from the source provider and items in the destination replica. Sync Framework provides objects that perform most of the work required to detect and handle conflicts.

Sync Framework detects conflicts at the level of the item or change unit. Sync Framework recognizes two categories of conflicts that can occur during synchronization: concurrency conflicts and constraint conflicts. Concurrency conflicts occur when the same item or change unit is changed on two different replicas that are later synchronized. Constraint conflicts are conflicts that violate constraints that are put on items or change units, such as the relationship of folders or the location of identically named data within a file system. Sync Framework divides constraint conflicts into the following three kinds.

  • A collision conflict occurs when the item cannot be saved because it conflicts with another item in the destination store, such as when the source provider sends a file that has the same name and location as a file that already exists in the destination replica.

  • A missing parent conflict occurs when an item cannot be saved in a hierarchical data store because it requires a parent item that does not exist, such as when the source provider sends a file to be saved in a directory that does not exist on the destination replica.

  • Other constraint conflicts occur when the item to be saved violates a constraint of the destination replica, such as when the source provider sends a file that is too large to be saved on the destination replica or when the change violates some business logic on the destination replica.

Constraints are related to the specific capabilities of an item store, such as foreign key constraints that are common in databases. Simple providers support only collision constraint conflicts. For more information about conflict handling for standard custom providers, see Detecting and Resolving Constraint Conflicts.

Understanding Conflict Handling

To decide how to handle concurrency conflicts and constraint conflicts, you must answer two important questions:

  • Should conflicts be resolved automatically during synchronization, or should the application be notified when a conflict is detected so that the application can drive conflict resolution?

  • Should all conflicts be resolved by specifying that the source or destination wins, or is more sophisticated conflict handling required? For example, in a concurrency conflict, you might want to merge the source and destination data into a single item that is applied to both replicas.

After answering these questions, you can specify how Sync Framework should behave when it encounters a conflict:

  1. Specify a resolution policy for concurrency conflicts and collision constraint conflicts. The policy determines whether Sync Framework automatically resolves the conflict or whether the application defaults to responding to an event to handle the conflict.

    If you specify a policy other than the default, Sync Framework sets the appropriate conflict resolution action when a conflict occurs. For example, if you specify a policy of "source wins" for concurrency conflicts, the "source wins" action is set if a conflict of that type is detected during a synchronization session. If you accept the default for one or both of the resolution policies, the provider or the application must respond to the events that fire when a conflict is detected. The provider can respond by implementing the following methods:

    If the provider does not implement these methods, the following application callbacks are used so that the application can set the resolution action. If the application does not respond to these events, the resolution of the conflict is deferred until a subsequent synchronization session. In this situation, the conflict will never be resolved unless the conflicting data or the application changes.

    In response to the conflict, the provider or application must set a resolution action.

    In addition to setting the resolution action, you can also include custom code in the event handler. For example, you can display conflicting items in a user interface as they are processed.

  2. For some of the resolution actions that Sync Framework or the application sets, you must implement one or both of the following interfaces:

    For concurrency conflicts, the resolution methods that you implement for these interfaces are distinguished by the type of conflicts that they respond to, such as an update-update conflict. For constraint conflicts, the resolution methods that you implement are distinguished by the outcome of the resolution, such as renaming the source item.

    For concurrency conflicts, if the action is set to Merge (for managed code) or SRA_MERGE (for unmanaged code), you must implement the following methods that handle the three types of concurrency conflicts:

    The implementation should merge the conflicting items in a way that is appropriate for the replica and application, as long as there is one final item that represents the two conflicting items.

    For collision constraint conflicts, implement methods based on the actions that can be set:

Managed Code Example

In this sample, the conflict handling policies for concurrency conflicts and constraint conflicts are left as the default of ApplicationDefined. This means that the application will register for the ItemConflicting and ItemConstraint events and specify an action to resolve conflicts if they occur during synchronization processing. The following code example shows the event handlers that are specified in the constructor of MyFullEnumerationSimpleSyncProvider:

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

The following code example shows the event handlers setting the conflict resolution actions to 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

The following code example shows the MergeConstraintConflict method that is implemented to respond to a resolution action of Merge for a constraint conflict:

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

See Also

Concepts

Implementing a Simple Custom Provider
How to: Create a Managed Simple Provider