次の方法で共有


簡易プロバイダーの競合処理

アプリケーションは、可能な限り競合を回避するようにデザインしてください。これは、競合検出と解決により、複雑さが増し、処理とネットワーク トラフィックが増加するためです。アプリケーションによっては、競合を避けられない場合もあります。たとえば、営業部門のアプリケーションでは、2 人の営業担当者が 1 つの営業区域を共有し、両方の営業担当者が同じ顧客および注文のデータを更新する可能性があります。同期コミュニティ内の項目に対して行われた変更を正しく反映するためには、同期元プロバイダーから送信された項目と、同期先レプリカの項目との間で生じた競合を同期先プロバイダーが検出し、処理する必要があります。Sync Framework には、競合の検出と処理に必要な作業の大部分を実行するオブジェクトが備わっています。

Sync Framework では、競合が項目レベルまたは変更単位レベルで検出されます。Sync Framework では、同期中に発生する可能性のある "同時実行の競合" と "制約の競合" の 2 種類の競合が認識されます。同時実行の競合は、後で同期される 2 つの異なるレプリカで、同じ項目または変更単位が変更された場合に発生します。制約の競合は、項目または変更単位が持つ制約 (フォルダーの関係や、ファイル システムにおいて、まったく同じ名前が付けられたデータの場所など) に違反する競合です。制約の競合は、Sync Framework によって次の 3 種類に分けられます。

  • 衝突競合: 同期元プロバイダーが同期先レプリカに既に存在するファイルと同じ名前および場所のファイルを送信する場合など、同期先のストアにある別の項目との競合が原因で項目を保存できない場合に発生します。

  • 親の欠落に起因する競合: 必要な親項目が階層型のデータ ストアに存在せず、項目をストアに保存できない場合に発生します。同期先レプリカには存在しないディレクトリを保存先とするファイルが同期元プロバイダーから送信された場合などが該当します。

  • その他の制約の競合: 保存しようとしている項目が、同期先レプリカの制約に違反している場合に発生します。同期元プロバイダーから送信されたファイルが大きすぎて同期先レプリカに保存できない場合や、変更が同期先レプリカのビジネス ロジックに違反する場合などが該当します。

制約は項目ストアの特定の能力に関係します。たとえば、データベースにおける代表的な制約に外部キー制約があります。簡易プロバイダーは衝突制約の競合のみサポートします。標準のカスタム プロバイダーの競合処理の詳細については、「制約の競合の検出および解決」を参照してください。

競合処理について

同時実行の競合や制約の競合の処理方法を決めるには、次の 2 つの点を明確にしておく必要があります。

  • 同期中に競合を自動的に解決するのか、競合が検出されたときにアプリケーション側で通知を受け取り、アプリケーション主導で競合解決を行えるようにするのか。

  • 同期元か同期先のどちらか一方を優先させるように指定してすべての競合を解決するのか、もっと洗練された競合処理が必要か。たとえば、同時実行の競合の場合、同期元と同期先のデータを単一の項目としてマージし、両方のレプリカに適用することもできます。

以上の点が明確になったら、競合が検出されたときの Sync Framework の動作を指定することができます。具体的な指定内容は次のとおりです。

  1. 同時実行の競合と衝突制約の競合とに対する解決方法 (解決ポリシー) を指定します。Sync Framework が自動的に競合を解決するのか、アプリケーションがイベント処理として競合を処理するのか (既定) は、このポリシーによって決まります。

    既定以外のポリシーを指定した場合、適切な競合解決アクションが、競合の発生時に Sync Framework によって設定されます。たとえば、同時実行の競合に対して "同期元優先" のポリシーを指定した場合、同期セッション中、その種類の競合が検出されたときに、"同期元優先" アクションが設定されます。いずれかまたは両方の解決方法に対し、既定の設定をそのまま受け入れた場合、プロバイダーまたはアプリケーションが、競合の検出時に発生するイベントに応答する必要があります。プロバイダーは、次のメソッドを実装することによってイベントに応答できます。

    プロバイダーがこれらのメソッドを実装していない場合、次のアプリケーション コールバックを使用して、アプリケーション側で解決アクションを設定できます。アプリケーションがこれらのイベントに応答しなかった場合、後続の同期セッションまで、競合の解決は延期されます。この場合、競合しているデータまたはアプリケーションが変わるまでは、決して競合は解決されません。

    競合が発生した場合、プロバイダーまたはアプリケーションが解決アクションを設定する必要があります。

    解決アクションの設定に加え、カスタム コードをイベント ハンドラーに追加することもできます。たとえば、競合処理中に、競合している項目をユーザー インターフェイスに表示することが考えられます。

  2. Sync Framework またはアプリケーションによって設定される一部の解決アクションについては、次のいずれかまたは両方のインターフェイスを実装する必要があります。

    同時実行の競合の場合、これらのインターフェイスに対して実装する解決メソッドは、実際に対処することになる競合の種類 (更新と更新の競合など) によって決まります。制約の競合の場合、実装する解決メソッドは、解決の結果 (同期元項目の名前変更など) によって決まります。

    同時実行の競合に関して、アクションを Merge (マネージ コードの場合) または SRA_MERGE (アンマネージ コードの場合) に設定した場合、3 種類ある同時実行の競合を処理するため、次のメソッドを実装する必要があります。

    競合する項目のマージ処理を実装する際は、"競合する 2 つの項目を表す項目が最終的に 1 つだけ得られることを絶対条件とし、実際のレプリカとアプリケーションに合った適切な方法を用いる" ことが大切です。

    衝突制約の競合の場合は、設定可能なアクションに基づいてメソッドを実装します。

マネージ コードの例

このサンプルでは、同時実行の競合および制約の競合に対する競合処理ポリシーに、既定の 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

参照

概念

簡易カスタム プロバイダーの実装
マネージ簡易プロバイダーを作成する方法