How to: Report and Resolve Constraint Conflicts
This topic describes how to use a managed language to enable a standard custom provider to report and resolve constraint conflicts. Constraint conflicts are conflicts that violate constraints that are put on items, such as the relationship of folders or the location of identically named data within a file system. Sync Framework provides the NotifyingChangeApplier class to help process constraint conflicts.
For more information about constraint conflicts, see Detecting and Resolving Constraint Conflicts.
This topic assumes a basic familiarity with C# and Microsoft .NET Framework concepts.
The examples in this topic focus on the following Sync Framework classes and methods:
The NotifyingChangeApplier class.
The SaveChangeWithChangeUnits(ItemChange, SaveChangeWithChangeUnitsContext) method of the INotifyingChangeApplierTarget interface.
The RecordConstraintConflictForItem(SyncId, ConstraintConflictReason) method of the SaveChangeWithChangeUnitsContext class.
Understanding Constraint Conflicts
Constraint conflicts are detected by the destination provider during the change application phase of synchronization, typically in the SaveItemChange(SaveChangeAction, ItemChange, SaveChangeContext) or SaveChangeWithChangeUnits(ItemChange, SaveChangeWithChangeUnitsContext) method. When the destination provider detects a constraint conflict, it reports the conflict to the NotifyingChangeApplier object by using the RecordConstraintConflictForChangeUnit(ChangeUnitChange) or RecordConstraintConflictForItem(SyncId, ConstraintConflictReason) method. The change applier resolves the conflict according to either the collision conflict resolution policy set for the session, or the conflict resolution action set by the application for the specified conflict. The change applier then dispatches any necessary calls to the destination provider, such as SaveItemChange(SaveChangeAction, ItemChange, SaveChangeContext) or SaveChangeWithChangeUnits(ItemChange, SaveChangeWithChangeUnitsContext), so that the destination provider can apply the resolved conflict to the destination replica. Typical resolutions of constraint conflicts include renaming the source item or destination item so that the constraint conflict no longer occurs, or merging the contents of the two items into a single item.
.NET Framework 2.0 or later.
Reference to Microsoft.Synchronization.
Reference to Microsoft.Synchronization.MetadataStorage.
The example code in this topic shows how to enable the change applier to handle constraint conflicts, how to detect and report a constraint conflict during the change application phase of synchronization, how to set the resolution action by handling the ItemConstraint event in the synchronization application, and how to resolve the conflict by applying the resolution to the destination replica. The replica in this example stores contacts in a text file as a list of comma-separated values. The items to synchronize are the contacts that are contained in this file and the change units that are tracked are the fields within each contact. Contacts are uniquely identified in the contact store by a combination of the name and phone number fields. When a contact that has the same name and phone number is created locally on two different replicas, then when the replicas are synchronized a collision constraint conflict occurs.
Enabling the NotifyingChangeApplier Object to Process Constraint Conflicts
The following requirements must all be satisfied to enable the NotifyingChangeApplier object to successfully handle constraint conflicts.
Create an IConflictLogAccess Object
The change applier uses a conflict log to save temporary conflicts during synchronization so that the conflicts can be processed efficiently during the session. For replicas that do not otherwise store conflicts, Sync Framework provides the MemoryConflictLog class, which operates in memory and can be used to store temporary conflicts during the synchronization session. This example creates the in-memory conflict log when the session starts.
Pass the IConflictLogAccess Object to the NotifyingChangeApplier Object
The destination provider must call the ApplyChanges method during processing of its ProcessChangeBatch(ConflictResolutionPolicy, ChangeBatch, Object, SyncCallbacks, SyncSessionStatistics) method. Be aware that the form of ApplyChanges that is called must accept a collision conflict resolution policy and an IConflictLogAccess object. This example calls ApplyChanges(ConflictResolutionPolicy, CollisionConflictResolutionPolicy, ChangeBatch, IChangeDataRetriever, IEnumerableItemChange, SyncKnowledge, ForgottenKnowledge, INotifyingChangeApplierTarget, IConflictLogAccess, SyncSessionContext, SyncCallbacks), specifying a collision conflict resolution policy of ApplicationDefined and passing the in-memory conflict log that was created in BeginSession(SyncProviderPosition, SyncSessionContext).
The change applier calls the SaveConstraintConflict(ItemChange, SyncId, ConstraintConflictReason, Object, SyncKnowledge, Boolean) method of the INotifyingChangeApplierTarget2 interface to save temporary conflicts. This example adds the INotifyingChangeApplierTarget2 interface to the class that implements KnowledgeSyncProvider.
This example implements SaveConstraintConflict(ItemChange, SyncId, ConstraintConflictReason, Object, SyncKnowledge, Boolean) by using the MemoryConflictLog object to save temporary conflicts and by throwing an exception otherwise.
The change applier must be able to get the version of a destination item that is in conflict. To supply this, the destination provider implements the TryGetDestinationVersion(ItemChange, ItemChange) method of the INotifyingChangeApplierTarget interface. This method is optional when constraint conflicts are not reported; otherwise, it is required.
Reporting a Constraint Conflict
Constraint conflicts are detected by the destination provider during the change application phase of synchronization. This example uses change units, so constraint conflicts are reported in the SaveChangeWithChangeUnits(ItemChange, SaveChangeWithChangeUnitsContext) method by calling RecordConstraintConflictForItem(SyncId, ConstraintConflictReason) when a change action of Create causes an identity collision in the contact store.
The CanCreateContact method of the contact store that is called in the above example uses a private DetectIndexCollision method to detect whether there is an identity collision caused by the contact to create. An identity collision occurs when the contact to be created contains the same name and phone number fields as an already existing contact.
Setting the Resolution Action for a Constraint Conflict
When the destination provider specifies a collision conflict resolution policy of ApplicationDefined, the application must register a handler for the ItemConstraint event before it starts synchronization.
The ItemConstraint event handler determines how the conflict is resolved. This example displays the conflicting items to the user and asks how the conflict should be resolved.
Handling Constraint Conflict Resolutions
After the constraint conflict resolution action has been set by the application, the change applier makes any necessary changes to the metadata associated with the resolution, and dispatches a call to the destination provider so that it can apply the change to the destination replica. This example uses change units, so the change applier calls the SaveChangeWithChangeUnits(ItemChange, SaveChangeWithChangeUnitsContext) method of the destination provider. The three possible resolutions that were presented to the user by the application are handled in this method.
Complete Provider Code
For a complete listing of the provider code used in this document, see Example Code for Reporting and Resolving Constraint Conflicts.
Next, you might want to implement a conflict log that can persist conflicts beyond the end of the synchronization session. For more information about creating a conflict log, see Logging and Managing Conflicts.