Synchronizing Change Units
A change unit represents a subitem change, such as the telephone number field in an item that represents a contact card. By using change units, providers can more efficiently synchronize subitem changes. Some examples of providers that might take advantage of change units are Personal Information Manager (PIM) providers and providers that handle images with their metadata.
Change units enable compact subitem changes to be represented and allow for a finer degree of change tracking. This can reduce the number of conflicts that are raised when changes to the item are made.
Consider an item representing a contact card that is persisted on a file system. If the granularity of change tracking were at the item (file) level, any change to the file would be an item change, and all of the data in the contact would have to be transferred. By using change units, a provider can decide instead to detect changes and resolve data conflicts at the property level of the contact, such as First Name, Last Name, and Phone Number. In this case, if two replicas independently change different properties on a contact, such as when one modifies the e-mail address and another attaches an image, no conflict is detected at the item level and only the change unit data must be sent.
The set of change units effectively forms a schema in which the order of the change units is decided by the replicas that are synchronizing a particular schema. For example, replicas can decide to represent contact properties as follows:
Change Unit[0] = First Name
Change Unit[1] = Last Name
Change Unit[2] = Phone Number
The duration of a change unit is bound to the duration of the item. Unlike regular change items, change units cannot be deleted because the replicas have agreed that these are properties of an item.
Providers should not try to spontaneously create change units because unwanted effects might occur.
Change units can be added based on schema updates that occur out-of-band with a synchronization of the data. For this to work, the added change units must have a null value or a default value that is assumed by all replicas. The update version for the added change units will then be the creation version of the item until those change units are modified. By treating change unit additions in this manner, they appear to the application components to be no different from a change unit that existed from the very beginning that has never been modified.
When the source provider uses change units to represent subitems that are enumerated from the source replica, it sends only the change units that changed, instead of the whole item. Be aware that when an item contains change units, version information is only kept for each change unit and not for the item itself.
To determine which change units to send, the source provider uses the Contains or Contains method of the SyncKnowledge object from the destination provider. If a change unit change is not contained in the destination knowledge, the change must be included in the change batch sent by the source provider.
Change unit changes are contained within item changes that are added to the change batch. The ItemChange object can be created to contain the change unit changes by using the ItemChange constructor or change unit changes can be added by using AddChangeUnitChange.
To determine which change units to send, the source provider uses the ISyncKnowledge::ContainsChangeUnit method of the ISyncKnowledge object from the destination provider. If a change unit change is not contained in the destination knowledge, it must be included in the change batch sent by the source provider.
Change unit changes are contained within item changes that are added to the change batch. To add change unit changes, specify a non-NULL value for the ppChangeBuilder parameter of the ISyncChangeBatchBase::AddItemMetadataToGroup method. The returned ISyncChangeBuilder object can then be used to add change unit changes to the associated item change by using ISyncChangeBuilder::AddChangeUnitMetadata.
When the destination provider uses a change applier to help process a change batch in its ProcessChangeBatch (for managed code) or IKnowledgeSyncProvider::ProcessChangeBatch (for unmanaged code) method, the destination provider enumerates destination version information for each change that is received from the source provider. When a source change contains change unit changes, the destination provider must determine which, if any, change unit versions to include in the batch of destination versions. This decision depends on the kind of change from the source provider and whether the item is marked as deleted on the destination replica. The following table shows which version information the destination provider must send to the change applier.
|
Source change is a delete |
Source change is an update |
Destination item is deleted |
Destination item version only. Deletes are allowed only on whole items. Therefore, version information for a delete is tracked for the item. |
Destination item version only. Deletes are allowed only on whole items. Therefore, version information for a delete is tracked for the item. |
Destination item is not deleted |
All destination change unit versions. |
Destination change unit versions only for the change units that are enumerated from the source. |
When an application uses custom conflict resolution for changes that contain change units, generally it must set the conflict resolution action for the change unit conflict by using SetResolutionAction (for managed code) or IChangeConflict::SetResolveActionForChangeUnit (for unmanaged code).
However, when the conflict is caused by an update on one replica and a delete on the other replica, the application must specify the conflict resolution action for the item conflict by using SetResolutionAction (for managed code) or IChangeConflict::SetResolveActionForChange (for unmanaged code).
Typically, when a change contains change units, Sync Framework calls SaveChangeWithChangeUnits (for managed code) or ISynchronousNotifyingChangeApplierTarget::SaveChangeWithChangeUnits (for unmanaged code) to apply the change to the destination replica. However, when a conflict has occurred and been resolved so that the item is deleted, Sync Framework calls SaveItemChange (for managed code) or ISynchronousNotifyingChangeApplierTarget::SaveChange (for unmanaged code). This is because only whole items can be deleted and not individual change units.