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:
Managed Code | Unmanaged Code |
---|---|
|
Use the following approach to convert data for each provider:
Implement a converter for each provider.
Managed code Implement the two required SyncDataConverter methods: ConvertDataFromProviderFormat and ConvertDataToProviderFormat.
Unmanaged code Implement the two required ISyncDataConverter methods: ConvertDataFromProviderFormat and ConvertDataToProviderFormat.
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;
}
Public Class SampleConversion
Public Sub Execute()
Dim orchestrator As New SyncOrchestrator()
orchestrator.Direction = SyncDirectionOrder.Upload
orchestrator.LocalProvider = New SampleSyncProvider(localStore)
orchestrator.RemoteProvider = New SampleSyncProvider(remoteStore)
Dim localConverter As New DataConverter(Of DataObject1, DataObject2)()
Dim remoteConverter As New DataConverter(Of DataObject2, DataObject3)()
orchestrator.LocalDataConverter = localConverter
orchestrator.RemoteDataConverter = remoteConverter
orchestrator.Synchronize()
End Sub
Private localStore As String
Private remoteStore As String
End Class
Public Class DataConverter(Of SourceType As {IDataObject, New}, DestType As {IDataObject, New})
Inherits SyncDataConverter
Public Sub New()
End Sub
Public Overloads Overrides Function ConvertDataFromProviderFormat(ByVal loadChangeContext As LoadChangeContext, ByVal itemData As Object) As Object
Dim dataObj As SourceType = DirectCast(itemData, SourceType)
Dim returnData As New DestType()
returnData.Data = dataObj.Data
Return returnData
End Function
Public Overloads Overrides Function ConvertDataToProviderFormat(ByVal loadChangeContext As LoadChangeContext, ByVal itemData As Object) As Object
Dim dataObj As DestType = DirectCast(itemData, DestType)
Dim returnData As New SourceType()
returnData.Data = dataObj.Data
Return returnData
End Function
Private sourceType As Type
Private destType As Type
End Class
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;
}
Public Overloads Overrides Function TryConvertDataRetrieverFromProviderFormat(ByVal dataRetrieverIn As Object, ByVal changes As IEnumerable(Of ItemChange), ByRef dataRetrieverOut As Object) As Boolean
dataRetrieverOut = New ConvertedDataRetriever(Of SourceType, DestType)(dataRetrieverIn)
Return True
End Function
Public Overloads Overrides Function TryConvertDataRetrieverToProviderFormat(ByVal dataRetrieverIn As Object, ByVal changes As IEnumerable(Of ItemChange), ByRef dataRetrieverOut As Object) As Boolean
dataRetrieverOut = New ConvertedDataRetriever(Of DestType, SourceType)(dataRetrieverIn)
Return True
End Function
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;
}
Public Class ConvertedDataRetriever(Of SourceType As {IDataObject, New}, DestType As {IDataObject, New})
Implements IChangeDataRetriever
Public Sub New(ByVal dataRetriever As Object)
Me.dataRetriever = dataRetriever
End Sub
Public ReadOnly Property IdFormats() As SyncIdFormatGroup
Get
Return DirectCast(dataRetriever, IChangeDataRetriever).IdFormats
End Get
End Property
Public Function LoadChangeData(ByVal loadChangeContext As LoadChangeContext) As Object
Dim iRetriever As IChangeDataRetriever = DirectCast(dataRetriever, IChangeDataRetriever)
Dim tempData As Object = iRetriever.LoadChangeData(loadChangeContext)
If tempData IsNot Nothing Then
Return ConvertData(tempData)
End If
Return Nothing
End Function
Private Function ConvertData(ByVal itemData As Object) As Object
Dim dataObj As SourceType = DirectCast(itemData, SourceType)
Dim returnData As New DestType()
returnData.Data = dataObj.Data
Return returnData
End Function
Private dataRetriever As Object
End Class