在提供程序间转换数据
在很多应用程序中,所有提供程序只同步格式相同的同类型数据。例如,几个数据库同步提供程序可能为 ADO.NET 数据集中的一组表同步和传输数据。但是,数据格式在某些情况下可能不同。请考虑同步联系人的应用程序。该应用程序使用两个开发人员分别编写的自定义提供程序。这些提供程序所需的数据在以下两个方面存在区别:
提供程序 A 将数据作为字节流传输,而提供程序 B 则将数据作为 XML 流传输。
提供程序 A 的数据包括三个字段:FirstName、LastName 和 PhoneNumber。提供程序 B 的数据包括 FirstName 和 LastName,并将 PhoneNumber 拆分为 PhoneNumber 和 AreaCode。
为了解决此问题,Sync Framework 允许您实现接口,用于将数据转换为每个提供程序所需的格式。针对本文所述的情形,您可以编写一个执行两次转换的转换器:首先将字节流输入转换为 XML 输出,然后将 XML 输入转换为字节流输出。Sync Framework 不要求为两个提供程序都指定转换器。您实现的数据转换代码由同步会话调用,这样数据转换在变更应用过程中对于目标提供程序是透明的。
还考虑这样的情形:大量具有不同数据格式的提供程序需要相互同步数据。一个方法是为每对提供程序编写一个转换器,但是这样做很难管理。一个更好的替代方法是为每个提供程序编写转换器,用于在提供程序数据格式和中间格式之间相互转换。在这种情况下,执行两次数据转换:从源提供程序格式到中间格式以及从中间格式到目标提供程序格式。
下表显示 Sync Framework 为数据转换提供的接口和属性:
托管代码 | 非托管代码 |
---|---|
|
使用以下方法为每个提供程序转换数据:
为每个提供程序实现一个转换器。
托管代码 - 实现两个必需的 SyncDataConverter 方法:ConvertDataFromProviderFormat 和 ConvertDataToProviderFormat。
非托管代码 - 实现两个必需的 ISyncDataConverter 方法:ConvertDataFromProviderFormat 和 ConvertDataToProviderFormat。
指定在同步会话期间要使用的转换器。
托管代码 - 指定作为两个 SyncOrchestrator 属性 LocalDataConverter 和 RemoteDataConverter 实现的转换器。
非托管代码 - 指定在两个 ISyncDataConversionControl 方法 SetSourceDataConverter 和 SetDestinationDataConverter 中实现的转换器。
在大多数情况下,您将只实现四个可用转换方法中的两个并为同步会话指定它们。只有在您使用的数据检索器不是 IChangeDataRetriever 的实现(对于托管代码)或者不是 IAsynchronousDataRetriever 或 ISynchronousDataRetriever 的实现(对于非托管代码)时,才需要与数据检索器转换有关的两个方法。对于将数据转换为中间状态的情况,必须使用 Sync Framework 数据检索器接口的实现。
Sync Framework 支持其中一个或两个提供程序是非托管的托管应用程序的数据转换。请注意转换过程始终由托管对象处理。如果应用程序是非托管的,同步时间将变长。
代码示例
以下代码示例演示如何同步两个要求进行数据转换的提供程序。在 Execute
方法中,代码首先实例化将进行同步的提供程序,然后实例化接受输入和输出格式的数据转换类的两个实例。在本示例中,方法 ConvertDataFromProviderFormat
和 ConvertDataToProviderFormat
返回未更改的数据,但在实际应用中,您要将输入格式转换为适当的输出格式。
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
下面的代码示例定义数据检索器转换方法 TryConvertDataRetrieverFromProviderFormat
和 TryConvertDataRetrieverToProviderFormat
。这两个方法都接受数据检索器和变更列表。为了执行数据检索器转换,它们实例化自 IChangeDataRetriever
继承的 ConvertedDataRetriever
示例类。
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
下面的代码示例创建 ConvertedDataRetriever
类并定义 LoadChangeData
方法和 IdFormats
属性。当转换数据检索器时,进行数据转换所需的任何代码都必须包含在 LoadChangeData
方法中或由该方法调用。
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