Share via


How to: Filter Enumerated Change Units

This topic describes how to use a managed language to filter the change units enumerated by a Sync Framework synchronization provider that synchronizes data from a custom data store.

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 members:

Understanding Change Unit Filtering

Change unit filters restrict synchronization data to a subset of change units, such as to only synchronize the name and phone number fields of a contact, ignoring the remaining change units. A change unit filter is useful when a replica only stores a subset of the change units defined for the items in the scope.

Sync Framework provides the ChangeUnitListFilterInfo object to define the list of change units to include in a change enumeration. The filter can be set by the synchronization application by using whatever mechanism is appropriate between the application and the provider, or it can be negotiated between the two providers. For more information on negotiating filters, see Filtering Synchronization Data.

When a change unit filter is defined, Sync Framework requests data only for the change units that are in the filter when it calls the LoadChangeData of the source provider.

The destination provider receives and applies changes the same was it does for a standard change batch, except that the data sent to SaveChangeWithChangeUnits contains only the change units that are in the filter.

Build Requirements

Example

The example code in this topic shows how to use the metadata storage service to produce a change batch that filters the change units that are included in the change batch. The replica in this example is a text file that stores contact information as a list of comma-separated values. The items to synchronize are the contacts that are contained in this file. The filter is defined to include only the name and phone number fields of the contact.

Setting the Filter

The source provider implements a public method that allows the application to set a change unit filter that defines which contact fields to include when enumerating changes.

public void SetContactFieldsToInclude(Contact.ChangeUnitFields[] includedFields)
{
    // Translate the array of fields to a list of IDs.
    _includedChangeUnits = new List<SyncId>(includedFields.Length);
    for (int iField = 0; iField < includedFields.Length; iField++)
    {
        _includedChangeUnits.Add(new SyncId((byte)includedFields[iField]));
    }

    _isFiltered = true;
}

The synchronization application creates the source provider as the local provider and uses the SetContactFieldsToInclude method to specify that only the name and phone number fields are to be included when enumerating changes. The application also creates the destination provider and performs synchronization.

private void SynchronizeWithChangeUnitFiltering(ContactStore localStore, ContactStore remoteStore, SyncDirectionOrder syncDir)
{
    // Create the local provider and set the change unit filter.
    // The filter is ignored when the provider is the destination provider.
    SyncProvider localProvider = new ContactsProviderChangeUnitFiltering(localStore);
    // Only include name and phone number fields.
    Contact.ChangeUnitFields[] includedFields = new Contact.ChangeUnitFields[2];
    includedFields[0] = Contact.ChangeUnitFields.NameCU;
    includedFields[1] = Contact.ChangeUnitFields.PhoneCU;
    ((ContactsProviderChangeUnitFiltering)localProvider).SetContactFieldsToInclude(includedFields);
    
    // Create the remote provider and do not set a filter.
    SyncProvider remoteProvider = new ContactsProviderChangeUnitFiltering(remoteStore);

    // Create the synchronization orchestrator and set the providers and synchronization direction.
    SyncOrchestrator orchestrator = new SyncOrchestrator();
    orchestrator.LocalProvider = localProvider;
    orchestrator.RemoteProvider = remoteProvider;
    orchestrator.Direction = syncDir;

    string msg;
    try
    {
        // Synchronize data between the two providers.
        SyncOperationStatistics stats = orchestrator.Synchronize();

        // Display statistics for the synchronization operation.
        msg = "Synchronization succeeded!\n\n" +
            stats.DownloadChangesApplied + " download changes applied\n" +
            stats.DownloadChangesFailed + " download changes failed\n" +
            stats.UploadChangesApplied + " upload changes applied\n" +
            stats.UploadChangesFailed + " upload changes failed";
    }
    catch (Exception ex)
    {
        msg = "Synchronization failed! Here's why: \n\n" + ex.Message;
    }
    MessageBox.Show(msg, "Synchronization Results");
}

Enumerating a Filtered Change Batch

The source provider implements GetChangeBatch so that, when a filter has been specified, it uses the metadata storage service to produce a filtered change batch. This is done by creating a ChangeUnitListFilterInfo object, initializing it with the specified list of change units to include. The filter information is passed to the GetFilteredChangeBatch method of the ReplicaMetadata object. The metadata storage service does not need to call back to the provider to determine what to include in the filter, so a null is specified for the filterCallback parameter.

public override ChangeBatch GetChangeBatch(uint batchSize, SyncKnowledge destinationKnowledge, out object changeDataRetriever)
{
    // Return this object as the IChangeDataRetriever object that is called to retrieve item data.
    changeDataRetriever = this;

    ChangeBatch retrievedBatch;
    if (_isFiltered)
    {
        // Use the metadata storage service to get a filtered batch of changes.
        ChangeUnitListFilterInfo filterInfo = new ChangeUnitListFilterInfo(IdFormats, _includedChangeUnits, true);
        retrievedBatch = _ContactStore.ContactReplicaMetadata.GetFilteredChangeBatch(batchSize, destinationKnowledge,
            filterInfo, null);
    }
    else
    {
        // Use the metadata storage service to get a batch of changes.
        retrievedBatch = _ContactStore.ContactReplicaMetadata.GetChangeBatch(batchSize, destinationKnowledge);
    }
    
    return retrievedBatch;
}

Next Steps

Next, you might want to add filter negotiation to your provider so that it can communicate with the destination provider to establish which filter to use for change enumeration. For more information on how to negotiate filters, see How to: Negotiate a Filter.

See Also

Concepts

Programming Common Standard Custom Provider Tasks
Filtering Synchronization Data