変更の適用

同期先プロバイダーによって変更バッチが取得され、競合が処理されたら、変更を同期先レプリカに適用する必要があります。通常、この処理は、同期先プロバイダーの ProcessChangeBatch (マネージ コードの場合) メソッドまたは ProcessChangeBatch (アンマネージ コードの場合) メソッドで実行され、Sync Framework の Change Applier オブジェクトを使用することで、非常に簡単に行うことができます。

変更の適用方法

同期先プロバイダーは、同期元プロバイダーから送られた変更バッチを受け取ると、それらの変更を同期先レプリカに適用します。Sync Framework の Change Applier オブジェクトは、NotifyingChangeApplier オブジェクト (マネージ コードの場合) を作成するか、IProviderSyncServices::CreateChangeApplier (アンマネージ コードの場合) を呼び出すことによって取得できます。ApplyChanges (マネージ コードの場合) メソッドまたは ApplyChanges (アンマネージ コードの場合) メソッドは、競合を検出し、同期先プロバイダーのメソッドを呼び出すことによって、変更を同期先レプリカに適用します。

変更項目の処理

通常、変更項目を処理する場合、Change Applier はまず、同期元プロバイダーの LoadChangeData (マネージ コードの場合) メソッドまたは ISynchronousDataRetriever::LoadChangeData (アンマネージ コードの場合) メソッドを呼び出して、データの転送を開始します。このメソッドからは、データ転送機構を表す object (マネージ コードの場合) または IUnknown インターフェイス (アンマネージ コードの場合) が返されます。次に、Change Applier は、同期先プロバイダーの SaveItemChange (マネージ コードの場合) メソッドまたは ISynchronousNotifyingChangeApplierTarget::SaveChange (アンマネージ コードの場合) メソッドを呼び出し、変更の保存コンテキストの一環としてデータ転送オブジェクトを渡します。これで、同期先プロバイダーがデータを同期先レプリカに転送できるようになります。データの取得または変更の処理の過程で発生したエラーは、RecordRecoverableErrorForItem (マネージ コードの場合) メソッドまたは ISaveChangeContext::SetRecoverableErrorOnChange (アンマネージ コードの場合) メソッドを使用して示されます。このメソッドは、この項目に対する復旧可能なエラーを、変更バッチに含まれる学習したナレッジ オブジェクトに記録します。制約の競合を使用する場合、RecordConstraintConflictForItem (マネージ コードの場合) または ISaveChangeContext2::SetConstraintConflictOnChange (アンマネージ コードの場合) を呼び出して制約の競合をレポートすることもできます。その後に、アプリケーションまたはプロバイダーの指定に従って制約の競合を解決できます。

学習したナレッジの更新

変更が適用されている間、Change Applier は、変更バッチに含まれている、学習したナレッジを更新します。学習したナレッジとは、変更バッチ内の変更に射影された、同期元レプリカのナレッジのことで、同期先レプリカが、変更バッチ内のすべての変更を適用した時点で学習することになるナレッジを表します。学習したナレッジは、次の方法で更新されます。

  • 中断やキャンセルが原因で、一部の変更しか適用されなかった場合、Change Applier は、射影操作を使用して、ナレッジを、適用されたその一部の変更のみに限定します。

  • また、適用できない変更があった場合、Change Applier は、それらをナレッジから除外します。

プロバイダーの開発者が、射影、和集合、および除外の各操作を実装する必要はありません。これらの操作は、プロバイダーに代わって、Change Applier が実行します。

更新された同期先ナレッジの保存

学習したナレッジが更新された後、そのナレッジは、同期先レプリカのナレッジと結合されます。同期先プロバイダーは、同期先レプリカのナレッジを、原子性を保ちながら、このナレッジに置き換える必要があります。原子性を保つには、更新されたナレッジをバッチごとに 1 回だけ (バッチ内のすべての変更が、単一のトランザクション内で適用された場合に) 保存するようにします。各バッチの最後に、同期先プロバイダーの StoreKnowledgeForScope (マネージ コードの場合) メソッドまたは ISynchronousNotifyingChangeApplierTarget::SaveKnowledge (アンマネージ コードの場合) メソッドを呼び出すことで、この処理を Change Applier に委ねることができます。このメソッドに渡すナレッジは、同期先レプリカに適用されることになる更新されたナレッジです。更新されたナレッジは、同期先プロバイダーから GetUpdatedDestinationKnowledge (マネージ コードの場合) メソッドまたは ISaveChangeContext::GetKnowledgeForScope (アンマネージ コードの場合) メソッドを呼び出すことによって 1 つずつ取得することができます。

プロバイダーが変更単位を使用してサブ項目を表している場合、変更の適用方法が若干異なります。詳細については、「変更単位の同期」を参照してください。

階層構造を持ったレプリカに関する注意事項

階層構造を持ったレプリカの同期では、同期元から同期先へと変更セットをバッチ単位に分割する際に、若干面倒な問題があります。Sync Framework では、データ階層の概念がサポートされません。したがって、この問題はすべてプロバイダー側で正しく対処する必要があります。

最も一般的な問題は、更新操作における親子関係の序列です。たとえば、次のシナリオを考えてみます。

  1. 同期元レプリカに新しいフォルダーが作成され、さらに、その中に一連の新しい項目が作成されたとします。

  2. 同期先プロバイダーが同期元プロバイダーに変更を要求します。同期元プロバイダーが、変更の一覧を 2 つのバッチに分けて送信します。しかし、親フォルダーの作成を含んだ変更バッチは、子項目を含んだ変更バッチよりも後に到着します。

  3. 同期先プロバイダーは、1 つ目のバッチで受け取った一連の項目の保存先を判断する必要があります。保存先の情報は、2 つ目のバッチが到着して初めて判明するためです。

階層構造に基づく更新の回避

更新時の親子関係に伴う煩雑さを抑える最も単純な方法は、同期元プロバイダーでグローバル ID の順序付けを行うことです。この方法により、親項目が常に子項目よりも先に到着するようになります。

同期先プロバイダーは、階層構造になったストアで生じた更新の転送処理を行う際、順序が不適切になった親子関係を受信する可能性があります。同期先プロバイダーには、こうした問題を解決できることが求められます。たとえば、順序が不適切になった変更を破棄し、ナレッジに例外を記録したり、後で適用するものとして変更をキューに追加するなどの方法が考えられます。項目のサイズが大きい場合もあるため、単に変更を破棄し、ナレッジに例外を記録する方法が、最も効率的なアプローチであると考えられます。

階層構造に基づく削除の回避

同期先プロバイダーは、項目がコンテナー項目であるかどうかを判断します。コンテナーが空である場合は、すぐに削除できます。ただし、コンテナーに、削除対象としてマークされていない項目が存在する場合、プロバイダーは、次のいずれかの処理を選択することになります。

  • 削除操作を後で行うようにキューに追加する。そのコンテナーのすべての子を削除対象としてマークした後、実際の削除操作をトリガーします。

  • この要求を破棄し、項目の受信順序が不適切になっていることを示す例外をナレッジに設定する。

階層の親が削除された後で子が追加される状況は避ける必要があります。"キューに追加された削除操作が有効なのは 2 者間のセッションの終わりまでであり、参加者全体では維持されない" という原則に注意してください。

参照

リファレンス

ISynchronousNotifyingChangeApplier インターフェイス
ISynchronousNotifyingChangeApplierTarget::SaveKnowledge
ISynchronousDataRetriever インターフェイス
ISaveChangeContext インターフェイス
NotifyingChangeApplier
StoreKnowledgeForScope
IChangeDataRetriever
SaveChangeContext

概念

標準のカスタム プロバイダーの実装