マネージ簡易プロバイダーを作成する方法

このトピックでは、Sync Framework SDK に付属する "Sync101 using Simple Sync Provider" サンプル アプリケーションの重要な部分について説明します。このアプリケーションでは、アンカー ベースの簡易プロバイダーと完全列挙の簡易プロバイダーの作成方法および同期方法を紹介します。このサンプル アプリケーションには、次の 3 つのクラスがあります。

  • MyFullEnumerationSimpleSyncProvider: FullEnumerationSimpleSyncProvider から派生します。

  • MyAnchorEnumerationSimpleSyncProvider: AnchorEnumerationSimpleSyncProvider から派生します。

  • MySimpleDataStore: メモリ内の項目データ ストアです。MySimpleDataStore はこのサンプル用のクラスであって、Sync Framework に属するクラスではありません。

どちらのプロバイダー クラスにも、ISimpleSyncProviderConcurrencyConflictResolver および ISimpleSyncProviderConstraintConflictResolver の競合処理インターフェイスが実装されています。

このトピックでは、このアプリケーションの次の点について説明します。

  • メタデータ ストアの作成

  • 項目ストアおよびメタデータ ストアにおける項目の識別

  • 項目の列挙とデータの読み込み

  • 2 つのプロバイダーの同期

  • 競合の処理

このトピックでは、データのフィルター選択やローカルのみを対象とした削除の方法についても簡単に説明します。

メタデータ ストアの作成

レプリカにはそれぞれのメタデータ ストアが必要です。簡易プロバイダーの場合、メタデータ ストアは 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 ランタイムは、メタデータ ストアの 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 オブジェクトは次の 3 つのプロパティを公開しています。

  • CustomFields

    カスタム フィールドは、メタデータ ストア内で整数によって識別されます。フィールドの表示名を必要とするアプリケーションは、この整数を名前に対応付ける必要があります。カスタム フィールドが定義されるのには、2 つの理由があります。1 つは項目を識別することであり、もう 1 つは、その項目に関するバージョン情報を提供することです。Sync Framework は、バージョン フィールドによって、特定の項目または変更単位が変更済みかどうかを判断します。この例では、バージョン フィールドに、項目ストアからの実際のデータが格納されます。そのため、項目ストア内のフィールドと同じだけ、フィールドが存在することになります。このような一対一の対応は、必ずしも必要ではなく、また、決して効率的ではありません。実際には、項目フィールドのハッシュを受け取り、それを単一のカスタム フィールドに格納する方が現実的です。

  • IdentityRules

    ID ルールは、項目を識別する際に、どのカスタム フィールドを使用するかを指定します。この場合、CUSTOM_FIELD_ID フィールド (フィールド 0) が使用されます。

  • ChangeUnitVersionDefinitions (このサンプルでは不使用)

    変更単位を使用する場合は、変更単位用のバージョン フィールドを定義する必要があります。変更単位とバージョン情報とが一対一で対応している必要はなく、また、実際のデータを格納しなければならないという要件もありません。さらに、変更単位が複数のフィールドにまたがっていてもかまいません。たとえば、このアプリケーションであれば、Zip Phone を 1 つの変更単位として指定し、さらに、Guid を別の変更単位として指定することが考えられます。Guid には、実際のデータを使用し、もう 1 つの変更単位については、Zip フィールドと Phone フィールドのハッシュを使用するなど、何らかのしくみを使ってバージョンを特定することができます。

項目ストア データに作用する一部のメソッド (InsertItem など) には、個々のフィールドを表す ItemField オブジェクトのコレクションが使用されます。これらのメソッドのパラメーターである ItemFieldDictionary オブジェクトは、CustomFieldDefinition オブジェクトで指定されたものと同じインデックス値を持ちます。

項目の列挙とデータの読み込み

Sync Framework は、同期元の項目ストア内の項目を列挙し、項目または変更単位が変更されているかどうかを検出した後、同期先ストアに適用できるよう、変更済みのデータを読み込むことができなければなりません。変更の検出は Sync Framework ランタイムによって処理されますが、変更の列挙およびデータの読み込みはストアに固有の処理であり、完全列挙プロバイダーの場合は、EnumerateItems および LoadChangeData を実装することによって行います。次のコード例は、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 によって列挙されたデータ変更を 1 つだけ含んだオブジェクトを返します。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 メソッド、InsertItem メソッド、および UpdateItem メソッドを実装することによって行います。次のコード例は、この各メソッドを実装したものです。

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

2 つのプロバイダーの同期

次のコード例は、同期元のレプリカと同期先のレプリカを同期する方法を示しています。このコードでは、同期元と同期先のプロバイダーを作成した後、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 をそのまま使用しています。つまり、アプリケーションで ItemConflicting イベントと ItemConstraint イベントに登録し、同期処理中に発生した競合の解決アクションを指定する必要があります。詳細については、「簡易プロバイダーの競合処理」を参照してください。次のコード例は、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

データのフィルター処理

特定の条件を満たしたデータだけを同期先に適用するために、アプリケーションでデータをフィルター選択しなければならないこともあります。たとえば、販売担当者にとっては、普段自分が取り扱っている商品についてのみ詳しい情報が入手できれば十分です。簡易プロバイダーにフィルター選択インターフェイスを実装し、使用するフィルターをネゴシエートすることによって、レプリカのデータをフィルター選択できるようになります。

次のコード例では、フィルターのネゴシエーション インターフェイスを使用して、同期セッション中に使用する特定のフィルターを決定します。フィルターのネゴシエーションを行うことで、同期先プロバイダーは、同期元プロバイダーに対し、1 つ以上のフィルターを使用して変更を列挙するように指定できます。同期元プロバイダーは、フィルターを受け入れることも、拒否することもできます。要求されたフィルターがいずれも同期元プロバイダーでサポートされていない場合、同期先プロバイダーでは、すべてのデータを受信し、自身でフィルター処理を行うことを選択できます。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); 
} 

参照

概念

簡易カスタム プロバイダーの実装