Converting Data Between Providers

In many applications, all providers synchronize exactly the same kind of data in the same format. For example, several database synchronization providers might synchronize and transfer data for a set of tables in ADO.NET data sets. However, data formats can be different in some situations. Consider an application that synchronizes contacts. The application uses custom providers written by two different developers. The data that these providers require is different in two ways:

  • Provider A transfers data as a byte stream, whereas provider B transfers data as an XML stream.

  • The data from provider A consists of three fields: FirstName, LastName, and PhoneNumber. The data from provider B includes FirstName and LastName, and divides PhoneNumber into PhoneNumber and AreaCode.

To address this scenario, Sync Framework lets you implement interfaces that convert data to the format that each provider requires. In this situation, you would write a converter that performs two conversions: the first converts a byte stream input to an XML output, and the second converts an XML input to a byte stream output. Sync Framework does not require that a converter be specified for both providers. The data conversion code that you implement is called by the synchronization session so that data conversion is transparent to the destination provider during the change application process.

Also consider a scenario in which a larger number of providers with different data formats need to synchronize data with each other. One approach is to write a converter for each pair of providers, but that can become unmanageable. A better alternative is to write converters for each provider that convert data to and from an intermediate format. In this situation, two data conversions are performed: from the source provider to the intermediate format, and from the intermediate format to the destination provider format.

The following table shows the interfaces and properties that Sync Framework provides for data conversion:

Use the following approach to convert data for each provider:

  1. Implement a converter for each provider.

  2. Specify the converters to be used during the synchronization session.

    • Managed code Specify the converters that you implemented as the two SyncOrchestrator properties: LocalDataConverter and RemoteDataConverter.

    • Unmanaged code Specify the converters that you implemented in the two ISyncDataConversionControl methods: SetSourceDataConverter and SetDestinationDataConverter.

In most situations, you will implement only two of the four available conversion methods and specify them for the synchronization session. The two methods related to data retriever conversion are required only if the data retriever that you use is not an implementation of IChangeDataRetriever (for managed code), or IAsynchronousDataRetriever or ISynchronousDataRetriever (for unmanaged code). For situations in which you convert data to an intermediate state, you must use an implementation of a Sync Framework data retriever interface.

Sync Framework supports data conversion for managed applications in which one or both providers are unmanaged. Be aware that the conversion process is always handled by managed objects. This can result in slower synchronization times than if the application is unmanaged.

Code Example

The following code example demonstrates how to synchronize two providers that require data conversion. In the Execute method, the code first instantiates the providers that will be synchronized and then instantiates two instances of the data conversion class that accepts an input and output format. In this example, the methods ConvertDataFromProviderFormat and ConvertDataToProviderFormat return data unchanged, but in a real application you would convert the input format to an appropriate output format.

public class SampleConversion
{
    public void Execute()
    {

        SyncOrchestrator orchestrator = new SyncOrchestrator();
        orchestrator.Direction = SyncDirectionOrder.Upload;
        orchestrator.LocalProvider = new SampleSyncProvider(localStore);
        orchestrator.RemoteProvider = new SampleSyncProvider(remoteStore);

        DataConverter<DataObject1, DataObject2> localConverter = new DataConverter<DataObject1, DataObject2>();

        DataConverter<DataObject2, DataObject3> remoteConverter = new DataConverter<DataObject2, DataObject3>();

        orchestrator.LocalDataConverter = localConverter;
        orchestrator.RemoteDataConverter = remoteConverter;

        orchestrator.Synchronize();
    }

    string localStore;
    string remoteStore;
}

public class DataConverter<SourceType, DestType> : SyncDataConverter
    where SourceType : IDataObject, new()
    where DestType : IDataObject, new()
{
    public DataConverter()
    {
    }

    public override object ConvertDataFromProviderFormat(LoadChangeContext loadChangeContext, object itemData)
    {
        SourceType dataObj = (SourceType)itemData;

        DestType returnData = new DestType();
        returnData.Data = dataObj.Data;

        return returnData;
    }

    public override object ConvertDataToProviderFormat(LoadChangeContext loadChangeContext, object itemData)
    {
        DestType dataObj = (DestType)itemData;

        SourceType returnData = new SourceType();
        returnData.Data = dataObj.Data;

        return returnData;
    }

    Type sourceType;
    Type destType;
}

The following code example defines the data retriever conversion methods TryConvertDataRetrieverFromProviderFormat and TryConvertDataRetrieverToProviderFormat. Both methods accept a data retriever and a list of changes. In order to perform data retriever conversion, they instantiate a ConvertedDataRetriever sample class that inherits from IChangeDataRetriever.

public override bool TryConvertDataRetrieverFromProviderFormat(
    object dataRetrieverIn,
    IEnumerable<ItemChange> changes,
    out object dataRetrieverOut)
{
    dataRetrieverOut = new ConvertedDataRetriever<SourceType, DestType>(dataRetrieverIn);
    return true;
}

public override bool TryConvertDataRetrieverToProviderFormat(
    object dataRetrieverIn,
    IEnumerable<ItemChange> changes,
    out object dataRetrieverOut)
{
    dataRetrieverOut = new ConvertedDataRetriever<DestType, SourceType>(dataRetrieverIn);
    return true;
}

The following code example creates the ConvertedDataRetriever class and defines the LoadChangeData method and the IdFormats property. When converting data retrievers, any code that is required for data conversion must be contained in or called by the LoadChangeData method.

public class ConvertedDataRetriever<SourceType, DestType> : IChangeDataRetriever
    where SourceType : IDataObject, new()
    where DestType : IDataObject, new()
{
    public ConvertedDataRetriever(object dataRetriever)
    {
        this.dataRetriever = dataRetriever;
    }

    public SyncIdFormatGroup IdFormats
    {
        get
        {
            return ((IChangeDataRetriever)dataRetriever).IdFormats;
        }
    }

    public object LoadChangeData(LoadChangeContext loadChangeContext)
    {
        IChangeDataRetriever iRetriever = (IChangeDataRetriever)dataRetriever;

        object tempData = iRetriever.LoadChangeData(loadChangeContext);

        if (tempData != null)
        {
            return ConvertData(tempData);
        }

        return null;
    }

    private object ConvertData(object itemData)
    {
        SourceType dataObj = (SourceType)itemData;

        DestType returnData = new DestType();
        returnData.Data = dataObj.Data;

        return returnData;
    }

    object dataRetriever;
}

See Also

Show: