다음을 통해 공유


방법: 관리되는 단순 공급자 만들기

이 항목에서는 Sync Framework SDK에 포함된 "Sync101 using Simple Sync Provider" 샘플 응용 프로그램의 중요한 부분에 대해 설명합니다. 이 응용 프로그램은 앵커 기반 및 전체 열거형 단순 공급자를 만들고 동기화하는 방법을 보여 줍니다. 예제 응용 프로그램에는 다음과 같은 세 개의 클래스가 있습니다.

  • FullEnumerationSimpleSyncProvider에서 파생되는 MyFullEnumerationSimpleSyncProvider

  • AnchorEnumerationSimpleSyncProvider에서 파생되는 MyAnchorEnumerationSimpleSyncProvider

  • MySimpleDataStore는 메모리 내 항목 데이터 저장소입니다. MySimpleDataStore는 이 샘플에서 사용되는 클래스로 Sync Framework의 일부가 아닙니다.

두 공급자 클래스에서 충돌 처리 인터페이스 ISimpleSyncProviderConcurrencyConflictResolverISimpleSyncProviderConstraintConflictResolver를 구현합니다.

이 항목에서는 응용 프로그램의 다음 부분에 대해 설명합니다.

  • 메타데이터 저장소 만들기

  • 항목 저장소 및 메타데이터 저장소의 항목 식별

  • 항목 열거 및 데이터 로드

  • 두 공급자 동기화

  • 충돌 처리

이 항목에서는 데이터를 필터링하고 로컬 전용 삭제를 수행하는 방법에 대해서도 간략하게 설명합니다.

메타데이터 저장소 만들기

각 복제본에는 메타데이터 저장소가 필요합니다. 단순 공급자의 경우에는 SqlMetadataStore 인스턴스가 필요합니다. 다음 코드 예제에서는 MyFullEnumerationSimpleSyncProvider의 생성자에 저장소 옵션을 지정합니다.

public MyFullEnumerationSimpleSyncProvider(string name, MySimpleDataStore store)
{
    _name = name;
    _store = store;

    // Create a file to store metadata for all items and a file to store 
    // the replica ID.
    _replicaMetadataFile = Environment.CurrentDirectory + "\\" + _name.ToString() + ".Metadata";
    _replicaIdFile = Environment.CurrentDirectory + "\\" + _name.ToString() + ".Replicaid";

    // Set ReplicaIdFormat to use a GUID as an ID, and ItemIdFormat to use a GUID plus
    // an 8-byte prefix.
    _idFormats = new SyncIdFormatGroup();
    _idFormats.ItemIdFormat.IsVariableLength = false;
    _idFormats.ItemIdFormat.Length = 24;
    _idFormats.ReplicaIdFormat.IsVariableLength = false;
    _idFormats.ReplicaIdFormat.Length = 16;

    this.ItemConstraint += new EventHandler<SimpleSyncItemConstraintEventArgs>(OnItemConstraint);
    this.ItemConflicting += new EventHandler<SimpleSyncItemConflictingEventArgs>(OnItemConflicting);
}
Public Sub New(ByVal name As String, ByVal store As MySimpleDataStore)
    _name = name
    _store = store

    ' Create a file to store metadata for all items and a file to store 
    ' the replica ID. 
    _replicaMetadataFile = (Environment.CurrentDirectory & "\") + _name.ToString() & ".Metadata"
    _replicaIdFile = (Environment.CurrentDirectory & "\") + _name.ToString() & ".Replicaid"

    ' Set ReplicaIdFormat to use a GUID as an ID, and ItemIdFormat to use a GUID plus 
    ' an 8-byte prefix. 
    _idFormats = New SyncIdFormatGroup()
    _idFormats.ItemIdFormat.IsVariableLength = False
    _idFormats.ItemIdFormat.Length = 24
    _idFormats.ReplicaIdFormat.IsVariableLength = False
    _idFormats.ReplicaIdFormat.Length = 16

    AddHandler Me.ItemConstraint, AddressOf HandleItemConstraint
    AddHandler Me.ItemConflicting, AddressOf HandleItemConflicting
End Sub

다음 코드 예제에서는 저장소를 만듭니다.

private void InitializeMetadataStore()
{
    SyncId id = ReplicaId;

    // Create or open the metadata store, initializing it with the ID formats 
    // that are used to reference items and replicas.
    if (!File.Exists(_replicaMetadataFile))
    {
        _metadataStore = SqlMetadataStore.CreateStore(_replicaMetadataFile);
    }
    else
    {
        _metadataStore = SqlMetadataStore.OpenStore(_replicaMetadataFile);
    }
}
Private Sub InitializeMetadataStore()
    Dim id As SyncId = ReplicaId

    ' Create or open the metadata store, initializing it with the ID formats 
    ' that are used to reference items and replicas. 
    If Not File.Exists(_replicaMetadataFile) Then
        _metadataStore = SqlMetadataStore.CreateStore(_replicaMetadataFile)
    Else
        _metadataStore = SqlMetadataStore.OpenStore(_replicaMetadataFile)
    End If
End Sub

다음 코드 예제에서는 이 저장소를 공급자 속성으로 반환합니다.

public override MetadataStore GetMetadataStore(out SyncId replicaId, out System.Globalization.CultureInfo culture)
{
    InitializeMetadataStore();

    replicaId = ReplicaId;
    culture = CultureInfo.CurrentCulture;
    return _metadataStore;
}
Public Overrides Function GetMetadataStore(ByRef replicaId__1 As SyncId, ByRef culture As System.Globalization.CultureInfo) As MetadataStore
    InitializeMetadataStore()

    replicaId__1 = ReplicaId
    culture = CultureInfo.CurrentCulture
    Return _metadataStore
End Function

항목 저장소 및 메타데이터 저장소의 항목 식별

항목을 동기화하려면 Sync Framework에서 항목 저장소의 항목을 식별하고 해당 ID를 메타데이터 저장소의 내부 ID에 매핑할 수 있어야 합니다. 또한 마지막 동기화 세션 이후로 항목 버전이 변경되었는지 여부를 확인할 수 있어야 합니다. 버전이 변경되었고 대상 복제본에 해당 버전의 항목이 포함되어 있지 않으면 항목을 동기화해야 합니다. 항목 대신 변경 단위 수준에서 변경 내용이 동기화되었으면 Sync Framework에서 변경 단위 및 해당 버전을 식별할 수 있어야 합니다. 변경 단위는 연락처를 나타내는 항목의 전화 번호 필드와 같은 하위 항목의 변경 내용을 나타냅니다. 이 샘플에서는 변경 단위를 사용하지 않습니다.

메타데이터 저장소 ID 형식 지정

다음 코드에서는 MyFullEnumerationSimpleSyncProvider의 생성자 및 IdFormats 속성을 정의합니다. 이렇게 하면 Sync Framework Runtime에서 메타데이터 저장소 ID에 사용할 형식을 결정할 수 있습니다. 유연한 ID를 사용하지 않으면 Sync Framework에서 고정 형식을 사용하여 복제본, 항목 및 변경 단위를 식별합니다. 유연한 ID를 사용하면 ISimpleSyncProviderIdGenerator 메서드를 사용하여 ID가 생성됩니다.

public MyFullEnumerationSimpleSyncProvider(string name, MySimpleDataStore store)
{
    _name = name;
    _store = store;

    // Create a file to store metadata for all items and a file to store 
    // the replica ID.
    _replicaMetadataFile = Environment.CurrentDirectory + "\\" + _name.ToString() + ".Metadata";
    _replicaIdFile = Environment.CurrentDirectory + "\\" + _name.ToString() + ".Replicaid";

    // Set ReplicaIdFormat to use a GUID as an ID, and ItemIdFormat to use a GUID plus
    // an 8-byte prefix.
    _idFormats = new SyncIdFormatGroup();
    _idFormats.ItemIdFormat.IsVariableLength = false;
    _idFormats.ItemIdFormat.Length = 24;
    _idFormats.ReplicaIdFormat.IsVariableLength = false;
    _idFormats.ReplicaIdFormat.Length = 16;

    this.ItemConstraint += new EventHandler<SimpleSyncItemConstraintEventArgs>(OnItemConstraint);
    this.ItemConflicting += new EventHandler<SimpleSyncItemConflictingEventArgs>(OnItemConflicting);
}
public SyncId ReplicaId
{
    get 
    {
        if (_replicaId == null)
        {
            _replicaId = GetReplicaIdFromFile( _replicaIdFile);
        }

        return _replicaId; 
    }
}

public override SyncIdFormatGroup IdFormats
{
    get { return _idFormats; }
}
Public Sub New(ByVal name As String, ByVal store As MySimpleDataStore)
    _name = name
    _store = store

    ' Create a file to store metadata for all items and a file to store 
    ' the replica ID. 
    _replicaMetadataFile = (Environment.CurrentDirectory & "\") + _name.ToString() & ".Metadata"
    _replicaIdFile = (Environment.CurrentDirectory & "\") + _name.ToString() & ".Replicaid"

    ' Set ReplicaIdFormat to use a GUID as an ID, and ItemIdFormat to use a GUID plus 
    ' an 8-byte prefix. 
    _idFormats = New SyncIdFormatGroup()
    _idFormats.ItemIdFormat.IsVariableLength = False
    _idFormats.ItemIdFormat.Length = 24
    _idFormats.ReplicaIdFormat.IsVariableLength = False
    _idFormats.ReplicaIdFormat.Length = 16

    AddHandler Me.ItemConstraint, AddressOf HandleItemConstraint
    AddHandler Me.ItemConflicting, AddressOf HandleItemConflicting
End Sub
Public ReadOnly Property ReplicaId() As SyncId
    Get
        If _replicaId Is Nothing Then
            _replicaId = GetReplicaIdFromFile(_replicaIdFile)
        End If

        Return _replicaId
    End Get
End Property

Public Overrides ReadOnly Property IdFormats() As SyncIdFormatGroup
    Get
        Return _idFormats
    End Get
End Property

항목 필드 및 메타데이터 스키마 지정

Sync Framework는 MetadataSchema 속성에 의해 노출되는 ItemMetadataSchema 개체를 사용하여 항목 저장소 데이터 또는 사용자가 만든 추가 메타데이터를 내부 메타데이터 저장소 ID 및 버전에 매핑합니다. 다음 코드 예제에서는 ItemMetadataSchema 개체에 대한 입력을 제공합니다. 샘플 코드의 상수는 항목 저장소의 각 열에 대해 정수 값을 정의합니다. 이 값은 ItemMetadataSchema 개체의 사용자 지정 필드 정의 및 ID 규칙을 만들 때 사용됩니다.

public const uint CUSTOM_FIELD_ID = 1;
public const uint CUSTOM_FIELD_TIMESTAMP = 2;
public override ItemMetadataSchema MetadataSchema
{
    get
    {
        CustomFieldDefinition[] customFields = new CustomFieldDefinition[2];
        customFields[0] = new CustomFieldDefinition(CUSTOM_FIELD_ID, typeof(ulong));
        customFields[1] = new CustomFieldDefinition(CUSTOM_FIELD_TIMESTAMP, typeof(ulong));

        IdentityRule[] identityRule = new IdentityRule[1];
        identityRule[0] = new IdentityRule(new uint[] { CUSTOM_FIELD_ID });

        return new ItemMetadataSchema(customFields, identityRule);
    }
}
Public Const CUSTOM_FIELD_ID As UInteger = 1
Public Const CUSTOM_FIELD_TIMESTAMP As UInteger = 2
Public Overrides ReadOnly Property MetadataSchema() As ItemMetadataSchema
    Get
        Dim customFields As CustomFieldDefinition() = New CustomFieldDefinition(1) {}
        customFields(0) = New CustomFieldDefinition(CUSTOM_FIELD_ID, GetType(ULong))
        customFields(1) = New CustomFieldDefinition(CUSTOM_FIELD_TIMESTAMP, GetType(ULong))

        Dim identityRule As IdentityRule() = New IdentityRule(0) {}
        identityRule(0) = New IdentityRule(New UInteger() {CUSTOM_FIELD_ID})

        Return New ItemMetadataSchema(customFields, identityRule)
    End Get
End Property

ItemMetadataSchema 개체는 다음과 같은 세 가지 속성을 노출합니다.

  • CustomFields

    사용자 지정 필드는 정수로 식별되는 메타데이터 저장소의 필드입니다. 응용 프로그램에서 하나 이상의 필드에 대해 익숙한 이름이 필요하게 되면 정수를 이름에 매핑해야 합니다. 사용자 지정 필드를 정의하는 이유는 항목을 식별하고 이러한 항목에 대한 버전 정보를 제공하기 위해서입니다. 버전 필드를 사용하면 Sync Framework에서 항목 또는 변경 단위가 변경되었는지 여부를 확인할 수 있습니다. 이 예제에서는 버전 필드에 항목 저장소의 실제 데이터가 들어 있으므로 항목 저장소의 필드마다 필드가 하나씩 있습니다. 이 일대일 대응은 필수도 아니고 효율적이지도 않습니다. 보다 실용적인 해결책은 항목 필드의 해시를 가져와 단일 사용자 지정 필드에 저장하는 것입니다.

  • IdentityRules

    ID 규칙은 항목을 식별하는 데 사용할 사용자 지정 필드를 지정합니다. 이 경우 CUSTOM_FIELD_ID 필드(필드 0)가 사용됩니다.

  • ChangeUnitVersionDefinitions(이 샘플에서는 사용되지 않음)

    변경 단위가 사용되면 변경 단위에 대한 버전 필드를 정의해야 합니다. 변경 단위와 버전 정보 간에 일대일 매핑이 있어야 한다거나 실제 데이터가 저장되어 있어야 한다는 등의 요구 사항은 없습니다. 변경 단위는 여러 필드로 확장될 수도 있습니다. 예를 들어 이 응용 프로그램에서 Zip Phone을 변경 단위로, Guid를 다른 변경 단위로 지정할 수 있습니다. Guid에 실제 데이터를 사용하고 다른 변경 단위에 Zip Phone 필드의 해시 또는 다른 메커니즘을 사용하여 버전을 확인할 수 있습니다.

InsertItem 같이 항목 저장소 데이터에 사용되는 몇 가지 메서드에는 각 필드를 나타내는 ItemField 개체의 모음이 필요합니다. 이러한 메서드에 대한 매개 변수인 ItemFieldDictionary 개체는 CustomFieldDefinition 개체에 지정된 것과 동일한 인덱스 값을 갖습니다.

항목 열거 및 데이터 로드

Sync Framework에서는 원본 항목 저장소의 항목을 열거하고 항목 또는 변경 단위가 변경되었는지 검색한 다음 대상 저장소에 적용할 수 있도록 변경된 데이터를 로드할 수 있어야 합니다. 변경 내용 감지는 Sync Framework Runtime에서 처리되지만 변경 내용 열거와 데이터 로드는 저장소 관련 작업이므로 EnumerateItemsLoadChangeData를 구현하여 전체 열거형 공급자에 대해 처리됩니다. 다음 코드 예제에서는 MySimpleDataStore 개체에서 열거된 항목 목록을 반환합니다.

public override void EnumerateItems(FullEnumerationContext context)
{
    List<ItemFieldDictionary> items = new List<ItemFieldDictionary>();
    foreach (ulong id in _store.Ids)
    {
        items.Add(_store.CreateItemFieldDictionary(id));
    }
    context.ReportItems(items);
}
Public Overrides Sub EnumerateItems(ByVal context As FullEnumerationContext)
    Dim items As New List(Of ItemFieldDictionary)()
    For Each id As ULong In _store.Ids
        items.Add(_store.CreateItemFieldDictionary(id))
    Next
    context.ReportItems(items)
End Sub

다음 코드 예제에서는 EnumerateItems에서 열거된 데이터 변경 내용 중 하나가 포함된 개체를 반환합니다. Sync Framework는 모든 변경 내용이 로드될 때까지 이 메서드를 호출합니다.

public override object LoadChangeData(ItemFieldDictionary keyAndExpectedVersion, IEnumerable<SyncId> changeUnitsToLoad, RecoverableErrorReportingContext recoverableErrorReportingContext)
{
    IDictionary<uint, ItemField> expectedFields = (IDictionary<uint, ItemField>)keyAndExpectedVersion;
    ulong id = (ulong)expectedFields[CUSTOM_FIELD_ID].Value;
    return new ItemTransfer(id, _store.Get(id));
}
Public Overrides Function LoadChangeData(ByVal keyAndExpectedVersion As ItemFieldDictionary, ByVal changeUnitsToLoad As IEnumerable(Of SyncId), ByVal recoverableErrorReportingContext As RecoverableErrorReportingContext) As Object
    Dim expectedFields As IDictionary(Of UInteger, ItemField) = DirectCast(keyAndExpectedVersion, IDictionary(Of UInteger, ItemField))
    Dim id As ULong = CULng(expectedFields(CUSTOM_FIELD_ID).Value)
    Return New ItemTransfer(id, _store.[Get](id))
End Function

삽입, 업데이트 및 삭제 적용

Sync Framework에서 원본의 변경 내용을 검색하고 로드한 후 이러한 변경 내용 및 해당 메타데이터 변경 내용을 대상 복제본에 적용해야 합니다. 대상에 있는 메타데이터 변경 내용은 Sync Framework에서 처리되지만 데이터 변경 내용 적용은 저장소 관련 작업이므로 DeleteItem, InsertItemUpdateItem 메서드를 구현하여 처리됩니다. 다음 코드 예제에서는 이러한 메서드의 구현을 제공합니다.

public override void InsertItem(object itemData, 
    IEnumerable<SyncId> changeUnitsToCreate,
    RecoverableErrorReportingContext recoverableErrorReportingContext, 
    out ItemFieldDictionary keyAndUpdatedVersion, 
    out bool commitKnowledgeAfterThisItem)
{
    ItemTransfer transfer = (ItemTransfer)itemData;
    ItemData dataCopy = new ItemData(transfer.ItemData);

    // Check for duplicates, and record a constraint error if a duplicate is detected.
    if (!_store.Contains(transfer.Id))
    {
        _store.CreateItem(dataCopy, transfer.Id);
        keyAndUpdatedVersion = _store.CreateItemFieldDictionary(transfer.Id);
    }
    else
    {
        recoverableErrorReportingContext.RecordConstraintError(_store.CreateItemFieldDictionary(transfer.Id));
        keyAndUpdatedVersion = null;
    }
    commitKnowledgeAfterThisItem = false;
}
public override void UpdateItem(object itemData, 
    IEnumerable<SyncId> changeUnitsToUpdate, 
    ItemFieldDictionary keyAndExpectedVersion, 
    RecoverableErrorReportingContext recoverableErrorReportingContext, 
    out ItemFieldDictionary keyAndUpdatedVersion, 
    out bool commitKnowledgeAfterThisItem)
{
    ItemTransfer transfer = (ItemTransfer)itemData;
    ItemData dataCopy = new ItemData(transfer.ItemData);
    
    IDictionary<uint, ItemField> expectedFields = (IDictionary<uint, ItemField>)keyAndExpectedVersion;
    ulong idToUpdate = (ulong)expectedFields[CUSTOM_FIELD_ID].Value;

    if (_store.Contains(idToUpdate))
    {
        ulong timeStamp = _store.UpdateItem(idToUpdate, dataCopy);
        keyAndUpdatedVersion = _store.CreateItemFieldDictionary(transfer.Id);
    }
    else
    {
        // If the item to update does not exist, record an error on this change and 
        // continue with the rest of the session.
        recoverableErrorReportingContext.RecordRecoverableErrorForChange(new RecoverableErrorData(new Exception("Item not found in the store")));
        keyAndUpdatedVersion = null;
    }
    commitKnowledgeAfterThisItem = false;
}
public override void DeleteItem(ItemFieldDictionary keyAndExpectedVersion, 
    RecoverableErrorReportingContext recoverableErrorReportingContext, 
    out bool commitKnowledgeAfterThisItem)
{
    IDictionary<uint, ItemField> expectedFields = (IDictionary<uint, ItemField>)keyAndExpectedVersion;
    ulong id = (ulong)expectedFields[CUSTOM_FIELD_ID].Value;
    if (_store.Contains(id))
    {
        _store.DeleteItem(id);
    }
    else
    {
        // If the item to delete does not exist, record an error on this change and 
        // continue with the rest of the session.
        recoverableErrorReportingContext.RecordRecoverableErrorForChange(new RecoverableErrorData(new Exception("Item not found in the store")));
    }
    commitKnowledgeAfterThisItem = false;
}
Public Overrides Sub InsertItem(ByVal itemData As Object, ByVal changeUnitsToCreate As IEnumerable(Of SyncId), ByVal recoverableErrorReportingContext As RecoverableErrorReportingContext, ByRef keyAndUpdatedVersion As ItemFieldDictionary, ByRef commitKnowledgeAfterThisItem As Boolean)
    Dim transfer As ItemTransfer = DirectCast(itemData, ItemTransfer)
    Dim dataCopy As New ItemData(transfer.ItemData)

    ' Check for duplicates, and record a constraint error if a duplicate is detected. 
    If Not _store.Contains(transfer.Id) Then
        _store.CreateItem(dataCopy, transfer.Id)
        keyAndUpdatedVersion = _store.CreateItemFieldDictionary(transfer.Id)
    Else
        recoverableErrorReportingContext.RecordConstraintError(_store.CreateItemFieldDictionary(transfer.Id))
        keyAndUpdatedVersion = Nothing
    End If
    commitKnowledgeAfterThisItem = False
End Sub
Public Overrides Sub UpdateItem(ByVal itemData As Object, ByVal changeUnitsToUpdate As IEnumerable(Of SyncId), ByVal keyAndExpectedVersion As ItemFieldDictionary, ByVal recoverableErrorReportingContext As RecoverableErrorReportingContext, ByRef keyAndUpdatedVersion As ItemFieldDictionary, ByRef commitKnowledgeAfterThisItem As Boolean)
    Dim transfer As ItemTransfer = DirectCast(itemData, ItemTransfer)
    Dim dataCopy As New ItemData(transfer.ItemData)

    Dim expectedFields As IDictionary(Of UInteger, ItemField) = DirectCast(keyAndExpectedVersion, IDictionary(Of UInteger, ItemField))
    Dim idToUpdate As ULong = CULng(expectedFields(CUSTOM_FIELD_ID).Value)

    If _store.Contains(idToUpdate) Then
        Dim timeStamp As ULong = _store.UpdateItem(idToUpdate, dataCopy)
        keyAndUpdatedVersion = _store.CreateItemFieldDictionary(transfer.Id)
    Else
        ' If the item to update does not exist, record an error on this change and 
        ' continue with the rest of the session. 
        recoverableErrorReportingContext.RecordRecoverableErrorForChange(New RecoverableErrorData(New Exception("Item not found in the store")))
        keyAndUpdatedVersion = Nothing
    End If
    commitKnowledgeAfterThisItem = False
End Sub
Public Overrides Sub DeleteItem(ByVal keyAndExpectedVersion As ItemFieldDictionary, ByVal recoverableErrorReportingContext As RecoverableErrorReportingContext, ByRef commitKnowledgeAfterThisItem As Boolean)
    Dim expectedFields As IDictionary(Of UInteger, ItemField) = DirectCast(keyAndExpectedVersion, IDictionary(Of UInteger, ItemField))
    Dim id As ULong = CULng(expectedFields(CUSTOM_FIELD_ID).Value)
    If _store.Contains(id) Then
        _store.DeleteItem(id)
    Else
        ' If the item to delete does not exist, record an error on this change and 
        ' continue with the rest of the session. 
        recoverableErrorReportingContext.RecordRecoverableErrorForChange(New RecoverableErrorData(New Exception("Item not found in the store")))
    End If
    commitKnowledgeAfterThisItem = False
End Sub

두 공급자 동기화

다음 코드 예제에서는 원본 및 대상 복제본을 동기화하는 방법을 보여 줍니다. 원본 및 대상 공급자를 만든 후 코드에서 SyncOrchestrator 옵션을 설정하고 복제본을 동기화합니다.

Dim agent As New SyncOrchestrator()
agent.Direction = SyncDirectionOrder.DownloadAndUpload
agent.LocalProvider = providerA
agent.RemoteProvider = providerB
stats = agent.Synchronize()
SyncOrchestrator agent = new SyncOrchestrator();
agent.Direction = SyncDirectionOrder.DownloadAndUpload;
agent.LocalProvider = providerA;
agent.RemoteProvider = providerB;
stats = agent.Synchronize();

충돌 처리

이 샘플에서는 동시성 충돌 및 제약 조건 충돌에 대한 충돌 처리 정책이 ApplicationDefined의 기본값으로 유지되어 있습니다. 즉, 응용 프로그램에서 ItemConflictingItemConstraint 이벤트를 등록하고 동기화를 처리하는 동안 충돌이 발생할 경우 이를 해결하는 동작을 지정합니다. 자세한 내용은 단순 공급자의 충돌 처리를 참조하십시오. 다음 코드 예제에서는 MyFullEnumerationSimpleSyncProvider의 생성자에 지정된 이벤트 처리기를 보여 줍니다.

this.ItemConstraint += new EventHandler<SimpleSyncItemConstraintEventArgs>(OnItemConstraint);
this.ItemConflicting += new EventHandler<SimpleSyncItemConflictingEventArgs>(OnItemConflicting);
AddHandler Me.ItemConstraint, AddressOf HandleItemConstraint

다음 코드 예제에서는 충돌 해결 동작을 Merge로 설정하는 이벤트 처리기를 보여 줍니다.

void OnItemConstraint(object sender, SimpleSyncItemConstraintEventArgs e)
{
    // Set the resolution action for constraint conflicts.
    // In this sample, the provider checks for duplicates in InsertItem, and this event would
    // fire if a duplicate occurred. 
    e.SetResolutionAction(ConstraintConflictResolutionAction.Merge);
}

void OnItemConflicting(object sender, SimpleSyncItemConflictingEventArgs e)
{
    // Set the resolution action for concurrency conflicts.
    e.SetResolutionAction(ConflictResolutionAction.Merge);
}
Private Sub HandleItemConstraint(ByVal sender As Object, ByVal e As SimpleSyncItemConstraintEventArgs)
    ' Set the resolution action for constraint conflicts. 
    ' In this sample, the provider checks for duplicates in InsertItem, and this event would 
    ' fire if a duplicate occurred. 
    e.SetResolutionAction(ConstraintConflictResolutionAction.Merge)
End Sub

Private Sub HandleItemConflicting(ByVal sender As Object, ByVal e As SimpleSyncItemConflictingEventArgs)
    ' Set the resolution action for concurrency conflicts. 
    e.SetResolutionAction(ConflictResolutionAction.Merge)
End Sub

다음 코드 예제에서는 제약 조건 충돌에 대한 Merge 해결 동작에 응답하도록 구현된 MergeConstraintConflict 메서드를 보여 줍니다.

public void MergeConstraintConflict(object itemData, 
    ConflictVersionInformation conflictVersionInformation, 
    IEnumerable<SyncId> changeUnitsToMerge, 
    ItemFieldDictionary localConflictingItem, 
    ItemFieldDictionary keyAndExpectedVersion, 
    RecoverableErrorReportingContext recoverableErrorReportingContext, 
    out ItemFieldDictionary updatedKeyAndVersion)
{
    ItemTransfer transfer = (ItemTransfer)itemData;
    ItemData dataCopy = new ItemData(transfer.ItemData);

    // Combine the conflicting data.
    ItemData mergedData = (_store.Get(transfer.Id)).Merge((ItemData)dataCopy);

    // We are doing a merge so we must delete the old conflicting item from our store.
    ulong idConflicting = (ulong)localConflictingItem[CUSTOM_FIELD_ID].Value;

    _store.DeleteItem(idConflicting);

    // Now create the new merged data in the store.
    if (_store.Contains(transfer.Id))
    {
        _store.UpdateItem(transfer.Id, dataCopy);
    }
    else
    {
        _store.CreateItem(mergedData, transfer.Id);
    }

    updatedKeyAndVersion = _store.CreateItemFieldDictionary(transfer.Id);
}
Public Sub MergeConstraintConflict(ByVal itemData As Object, ByVal conflictVersionInformation As ConflictVersionInformation, ByVal changeUnitsToMerge As IEnumerable(Of SyncId), ByVal localConflictingItem As ItemFieldDictionary, ByVal keyAndExpectedVersion As ItemFieldDictionary, ByVal recoverableErrorReportingContext As RecoverableErrorReportingContext, _
ByRef updatedKeyAndVersion As ItemFieldDictionary) Implements ISimpleSyncProviderConstraintConflictResolver.MergeConstraintConflict
    Dim transfer As ItemTransfer = DirectCast(itemData, ItemTransfer)
    Dim dataCopy As New ItemData(transfer.ItemData)

    ' Combine the conflicting data. 
    Dim mergedData As ItemData = (_store.[Get](transfer.Id)).Merge(DirectCast(dataCopy, ItemData))

    ' We are doing a merge so we must delete the old conflicting item from our store. 
    Dim idConflicting As ULong = CULng(localConflictingItem(CUSTOM_FIELD_ID).Value)

    _store.DeleteItem(idConflicting)

    ' Now create the new merged data in the store. 
    If _store.Contains(transfer.Id) Then
        _store.UpdateItem(transfer.Id, dataCopy)
    Else
        _store.CreateItem(mergedData, transfer.Id)
    End If

    updatedKeyAndVersion = _store.CreateItemFieldDictionary(transfer.Id)
End Sub

데이터 필터링

일부 응용 프로그램에서는 특정 조건을 충족하는 데이터만 대상에 적용할 수 있도록 데이터를 필터링할 수 있어야 합니다. 예를 들어 정기적으로 판매하는 제품에 대해서만 자세한 제품 정보를 필요로 하는 영업 사원이 있을 수 있습니다. 단순 공급자를 사용하면 필터링 인터페이스를 구현하거나 필터 사용을 결정하여 복제본에서 데이터를 필터링할 수 있습니다.

다음 코드 예제에서는 필터 결정 인터페이스를 사용하여 동기화 세션 중에 특정 필터를 사용해야 하는지 여부를 결정합니다. 필터 협상을 사용하면 대상 공급자가 변경 내용 열거 중에 원본 공급자가 하나 이상의 필터를 사용하도록 지정할 수 있습니다. 원본 공급자는 필터를 허용하거나 거부할 수 있습니다. 원본 공급자가 요청된 필터 중 일부를 지원하지 않으면 대상 공급자가 모든 데이터를 받고 자체적으로 필터링을 수행하도록 선택할 수 있습니다. Sync Framework는 적절하게 공급자를 호출하여 필터 사용을 협상합니다.

public bool RequestFilter
{
    set
    {
        _requestFilter = value; 
    }
}
private bool _requestFilter = false;

void IRequestFilteredSync.SpecifyFilter(FilterRequestCallback filterRequest)
{
    // Request a filter only if this provider represents a filtered replica.
    if (_requestFilter)
    {
        if (!filterRequest("TheFilter", FilteringType.CurrentItemsOnly))
        {
            throw new SyncInvalidOperationException("Could not agree on filter.");
        }
    }
}

bool ISupportFilteredSync.TryAddFilter(object filter, FilteringType filteringType)
{
    if (!((string)filter).Equals("TheFilter"))
    {
        throw new Exception("Filter is incorrect");
    }

    // Remember the filter.
    _filter = (string)filter;

    return true;
}
private string _filter = "";
Public WriteOnly Property RequestFilter() As Boolean
    Set(ByVal value As Boolean)
        _requestFilter = value
    End Set
End Property

Private _requestFilter As Boolean = False

Private Sub SpecifyFilter(ByVal filterRequest As FilterRequestCallback) Implements IRequestFilteredSync.SpecifyFilter
    ' Request a filter only if this provider represents a filtered replica.
    If _requestFilter Then
        If Not filterRequest("TheFilter", FilteringType.CurrentItemsOnly) Then
            Throw New SyncInvalidOperationException("Could not agree on filter.")
        End If
    End If
End Sub

Private Function TryAddFilter(ByVal filter As Object, ByVal filteringType As FilteringType) As Boolean Implements ISupportFilteredSync.TryAddFilter
    If Not DirectCast(filter, String).Equals("TheFilter") Then
        Throw New Exception("Filter is incorrect")
    End If

    ' Remember the filter.
    _filter = DirectCast(filter, String)

    Return True
End Function

Private _filter As String = ""

다음 코드 예제에서는 우선 필터 옵션을 None으로 지정합니다. 즉, 대상에 이미 알려진 경우에도 항목을 필터링해야 합니다. 그런 다음 코드 예제에서 항목 필드 값 중 하나를 기준으로 항목을 필터링하는 IsItemInFilterScope 메서드를 구현합니다. 필터를 정의한 후 코드 예제에서 UseFilterThisSession 메서드를 구현합니다. 이렇게 하면 응용 프로그램에서 세션 단위로 필터링을 사용할지 여부를 지정할 수 있습니다.

SimpleSyncProviderFilterOptions IFilteredSimpleSyncProvider.FilterOptions
{
    get
    {
        return SimpleSyncProviderFilterOptions.None;
    }
}

bool IFilteredSimpleSyncProvider.IsItemInFilterScope(ItemFieldDictionary KeyAndVersion)
{
    ulong itemId = (ulong)KeyAndVersion[1].Value;
    ItemData itemData = _store.Get(itemId);
    if (itemData["data"] == "3333")
    {
        return false;
    }

    return true;
}

bool IFilteredSimpleSyncProvider.UseFilterThisSession
{
    get
    {
        // Indicate whether a filter has been requested and agreed upon for this session.
        return ("" != _filter);
    }
}
Private ReadOnly Property FilterOptions() As SimpleSyncProviderFilterOptions Implements IFilteredSimpleSyncProvider.FilterOptions
    Get
        Return SimpleSyncProviderFilterOptions.None
    End Get
End Property

Private Function IsItemInFilterScope(ByVal KeyAndVersion As ItemFieldDictionary) As Boolean Implements IFilteredSimpleSyncProvider.IsItemInFilterScope
    Dim itemId As ULong = KeyAndVersion(1).Value
    Dim data As ItemData = _store.Get(itemId)
    If data("data") Is "3333" Then
        Return False
    End If

    Return True
End Function

Private ReadOnly Property UseFilterThisSession() As Boolean Implements IFilteredSimpleSyncProvider.UseFilterThisSession
    Get
        ' Indicate whether a filter has been requested and agreed upon for this session.
        Return "" Is _filter
    End Get
End Property

로컬 전용 삭제 수행

일부 동기화 시나리오에서는 해당 삭제를 다른 복제본으로 전파하지 않고 로컬 복제본에서 항목을 삭제할 수 있는 기능이 필요합니다. 예를 들어 서로 다른 영업 사원의 정보를 저장하는 몇 가지 장치와 서버를 동기화할 수 있습니다. 각 장치의 공간은 제한되어 있으므로 영업 사원은 장치에서 오래 전에 완료된 주문을 삭제합니다. 서버에서 이 데이터를 필요로 할 수 있으므로 이러한 종류의 삭제는 서버로 전파되면 안 됩니다. 단순 공급자에서는 로컬로 삭제해야 하는 데이터를 지정할 수 있습니다. 세션 단위로 삭제 동작을 제어하려면 SetDeleteMode를 사용하여 적절한 옵션을 지정합니다. 다음 코드 예제에서는 동기화 중에 삭제가 전파되지 않도록 지정합니다.

public override void EnumerateItems(FullEnumerationContext context)
{

    context.SetDeleteMode(SimpleSyncProviderDeleteMode.LocalOnly);

    List<ItemFieldDictionary> items = new List<ItemFieldDictionary>();
    foreach (ulong id in _store.Ids)
    {
        items.Add(_store.CreateItemFieldDictionary(id));
    }
    context.ReportItems(items);
}
public override void EnumerateItems(FullEnumerationContext context) 
{ 

context.SetDeleteMode(SimpleSyncProviderDeleteMode.LocalOnly); 

List<ItemFieldDictionary> items = new List<ItemFieldDictionary>(); 
foreach (ulong id in _store.Ids) 
{ 
items.Add(_store.CreateItemFieldDictionary(id)); 
} 
context.ReportItems(items); 
} 

참고 항목

개념

단순 사용자 지정 공급자 구현