Поделиться через


Преобразование данных между поставщиками

Во многих приложениях все поставщики выполняют синхронизацию одних и тех же данных в одном и том же формате. Например, несколько служб синхронизации базы данных могут синхронизировать и передавать данные для набора таблиц в наборах данных ADO.NET. Однако в некоторых ситуациях форматы данных могут различаться. Рассмотрим приложение, которое синхронизирует сведения о контактах. В приложении используются пользовательские поставщики, написанные двумя разными разработчиками. Данные, необходимые этим поставщикам, отличаются друг от друга по следующим причинам.

  • Поставщик A передает данные как поток байтов, а поставщик B передает данные как поток XML.

  • Данные от поставщика A состоят из трех полей: FirstName, LastName и PhoneNumber. Данные от поставщика B включают поля FirstName и LastName, а поле PhoneNumber подразделяется на PhoneNumber и AreaCode.

Чтобы реализовать этот сценарий, Sync Framework предусматривает возможность реализации интерфейсов для преобразования данных в формат, необходимый каждому из поставщиков. В этой ситуации необходимо написать код преобразователя, выполняющего два преобразования: из байтового потока в XML-данные и из XML-данных в байтовый поток. Sync Framework не требует указания преобразователя для обоих поставщиков. Реализованный код преобразования данных будет вызываться в сеансе синхронизации, чтобы обеспечить прозрачность преобразования данных для поставщика назначения во время процесса применения изменений.

Рассмотрим также сценарий, в котором множество поставщиков с разными форматами данных должны проводить синхронизацию данных друг с другом. Один подход состоит в том, чтобы написать преобразователь для каждой пары поставщиков, однако попытка его применить может оказаться неосуществимой. Целесообразней написать для каждого поставщика код преобразователя, выполняющий прямое и обратное преобразование данных в промежуточный формат. В этом случае будут выполняться два преобразования данных: из формата поставщика источника в промежуточный формат и из промежуточного формата в формат поставщика назначения.

В следующей таблице приведены предусмотренные в Sync Framework интерфейсы и свойства для преобразования данных.

Управляемый код Неуправляемый код

SyncDataConverter

Интерфейс ISyncDataConverter

LocalDataConverter

Интерфейс ISyncDataConversionControl

RemoteDataConverter

 

Чтобы преобразовать данные для каждого из поставщиков, пользуйтесь следующим подходом:

  1. Реализация преобразователя для каждого поставщика.

    • Управляемый код Реализуйте два обязательных метода SyncDataConverter: ConvertDataFromProviderFormat и ConvertDataToProviderFormat.

    • Неуправляемый код Реализуйте два обязательных метода ISyncDataConverter: ConvertDataFromProviderFormat и ConvertDataToProviderFormat.

  2. Указание преобразователей, используемых во время сеанса синхронизации.

    • Управляемый код Укажите преобразователи, реализованные как свойства 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. Оба метода принимают объект получения данных и список изменений. Для преобразования получателя данных создается экземпляр образца класса ConvertedDataRetriever, наследуемого из 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

В следующем примере кода создается класс 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

См. также

Основные положения

Объединение данных от различных поставщиков