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:
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.
Managed Code: The provider specifies values from the ConflictResolutionPolicy and CollisionConflictResolutionPolicy enumerations for the KnowledgeSyncProviderConfiguration object that is exposed by the Configuration property.
Unmanaged Code: When the application starts a synchronization session, CONFLICT_RESOLUTION_POLICY is passed to ISyncSession::Start. In this release, you cannot pass COLLISION_CONFLICT_RESOLUTION_POLICY for unmanaged code.
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.
Managed Code: Call SetResolutionAction(ConflictResolutionAction) and pass a value from ConflictResolutionAction, or call SetResolutionAction(ConstraintConflictResolutionAction) and pass a value from ConstraintConflictResolutionAction.
Unmanaged Code: Call IChangeConflict::SetResolveActionForChange or IChangeConflict::SetResolveActionForChangeUnit and pass a value from SYNC_RESOLVE_ACTION, or call IConstraintConflict::SetConstraintResolveActionForChange or IConstraintConflict::SetConstraintResolveActionForChangeUnit and pass a value from SYNC_CONSTRAINT_RESOLVE_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.
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:
Managed Code: ResolveUpdateUpdateConflict(Object, IEnumerableSyncId, IEnumerableSyncId, ItemFieldDictionary, RecoverableErrorReportingContext, ItemFieldDictionary), ResolveLocalDeleteRemoteUpdateConflict(Object, IEnumerableSyncId, RecoverableErrorReportingContext, Boolean, ItemFieldDictionary), and ResolveLocalUpdateRemoteDeleteConflict(ItemFieldDictionary, RecoverableErrorReportingContext, Boolean).
Unmanaged Code: ISimpleSyncProviderConcurrencyConflictResolver::ResolveUpdateUpdateConflict, ISimpleSyncProviderConcurrencyConflictResolver::ResolveLocalDeleteRemoteUpdateConflict, and ISimpleSyncProviderConcurrencyConflictResolver::ResolveLocalUpdateRemoteDeleteConflict.
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 (ConstraintConflictResolutionAction):
Unmanaged Code (SYNC_CONSTRAINT_RESOLVE_ACTION):
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:
The following code example shows the event handlers setting the conflict resolution actions to Merge:
The following code example shows the MergeConstraintConflict(Object, ConflictVersionInformation, IEnumerableSyncId, ItemFieldDictionary, ItemFieldDictionary, RecoverableErrorReportingContext, ItemFieldDictionary) method that is implemented to respond to a resolution action of Merge for a constraint conflict: