プロバイダー ホスト型アドインでアドイン イベントを処理する
これは、プロバイダー ホスト型の SharePoint アドインの開発の基本に関する記事のシリーズの 7 番目です。SharePoint アドイン とこのシリーズの前の記事 (プロバイダー ホスト型の SharePoint アドインの作成を始めるにある記事) をよく理解しておいてください。
注:
プロバイダー ホスト型アドインに関するこのシリーズに沿って作業してきた場合は、このトピックでも引き続き使用できる Visual Studio ソリューションを既に所有しています。 また、SharePoint_Provider-hosted_Add-Ins_Tutorials でリポジトリをダウンロードして BeforeAdd-inEventHandlers.sln ファイルを開くこともできます。
この記事では、アドイン イベントと呼ばれる種類の SharePoint のイベントの処理をカスタマイズします。 具体的には、アドインのインストール イベントとアンインストール イベントのハンドラーを作成します。 リストとリスト アイテム イベントの処理もカスタマイズできます。これについては、このシリーズの今後の記事で説明します。 このようなイベントのすべては SharePoint でトリガーされますが、それぞれのイベントを処理するカスタム コードはリモート Web アプリケーション内に存在します。 SharePoint イベントにハンドラーの URL を登録することで、カスタム ハンドラーを呼び出すように SharePoint を構成します。
プログラムを使用して SharePoint コンポーネントを配置する 2 つの場所
チェーン ストア アドインでは、[現地の従業員] リストと [出荷予定] リストを自動的に作成して展開するようにします。 アドインでは、カスタム リストなどの SharePoint コンポーネントをいつでも展開できます。 ただし、アドインがカスタム リストなどの特定のコンポーネントに依存しているときには、実際にはユーザーがアドインの操作を開始する前に、そのコンポーネントが展開されている必要があります。 そのような重要なコンポーネントのために、カスタムの展開ロジックを実行できる場所が 2 つあります。
- アドインのインストール イベントのハンドラー内。
- アドインが初めて SharePoint で起動されたときに実行される「初回実行」ロジック内。
特定のアドインにとってどちらが最適かを決定するというのは複雑な話になります。 この記事では、いくつかの比較点のみを扱います。
カスタム インストール ハンドラーは、30 秒以内に完了する必要があります。 初回実行ロジックには時間の制限はありません。
アドインのインストール時に問題が発生した場合、SharePoint はインストールの一環として実行したすべての操作をロールバックします。 カスタムのインストール ハンドラーは、このシステムにカスタムのハンドラーが参加できるように、SharePoint がアドインをインストールするための操作をすべて完了した後で実行します。
たとえば、カスタム ロジックで例外をスローすると、SharePoint にアドインのインストール全体をロールバックするように通知できます。 ただし、カスタムの初回実行ロジックで問題が発生すると、アドインはインストールされたままになり、通常は適切に動作しなくなります。
SharePoint は、アドインのインストールをロールバックする必要があってもインストールを放棄しません。 すぐに、インストールの再試行を開始します。 最大 4 回までの再試行が実行されます (試行ごとに 30 秒間の制限が適用されます)。 再試行のたびに、カスタムのインストール ハンドラーは最初から実行を再開します。 たとえば、ハンドラーがロールバックの前にリストをインストールしようとしていた場合は、再試行時に同じリストを再度インストールしようとします。
こうした事態が発生しないようにするには、インストール ハンドラーのコードが、一切の操作 (コンポーネントを展開するなどの操作) を実行しないように記述されている必要があります。ただし、最初に、その操作が既に実行されているかどうかを確認している場合を除きます。 このため、インストール ハンドラーのロジックは初回実行ロジックよりも複雑になります。初回実行ロジックは再試行することがないためです (再試行するための具体的なコードを記述していない場合)。 さらに、コンポーネントが既に展開されたかどうかを確認するには、通常はリモート ハンドラーから SharePoint への時間がかかるインターネット経由での呼び出しも必要になります。 実際にコンポーネントを展開するには 2 回目の呼び出しも必要になります (まだコンポーネントが展開されていなかった場合)。
チェーン ストア アドインでは、これらの戦略を組み合わせています。 この記事で作成するインストール ハンドラーでは、企業のデータベースにテナントとしてホスト Web を登録して、ホスト Web でアドインが実行されているかどうかを指定する通知を設定します。
このシリーズの今後の記事では、アドインの開始ページの Page_Load メソッドに初回実行ロジックを組み込みます。 このロジックでは、2 つのカスタム リストを展開します。また、その他の操作も実行します。
イベント レシーバーのデバッグ用のソリューションを構成する
イベント レシーバーのデバッグには、Azure Service Bus を使用する必要があります。 「SharePoint アドインでのリモート イベント レシーバーのデバッグとトラブルシューティング」の手順を実行してください。 テスト用のサイトとして SharePoint Online の Web サイトを使用するため、リモートのテスト用サイトの手順を必ず実行してください。 このシリーズの今後の記事では、デバッグの構成が正常に完了していることが前提になります。
インストール ハンドラーを作成する
注:
Visual Studio のスタートアップ プロジェクトの設定は、ソリューションが開かれるたびに、既定値に戻される傾向があります。 このシリーズ記事のサンプル ソリューションを再開した直後は、次の手順を必ず実行してください。
- ソリューション エクスプローラーの上部にあるソリューション ノードを右クリックして、[スタートアップ プロジェクトの設定] を選択します。
- 3 つすべてのプロジェクトが [アクション] 列で [開始] に設定されていることを確認します。
ソリューション エクスプローラーで、ChainStore プロジェクトを選択すると、そのプロパティが Visual Studio の [プロパティ] ウィンドウに表示されます。
[アドインのインストールの処理] の値を [True] に設定します (これは、まだ [アプリのハンドルがインストールされました] になっている可能性もあります)。 これにより、次に示す 2 つの作業が実行されます。
[サービス] という名前のフォルダーが [ChainStoreWeb] プロジェクト ([ChainStore] プロジェクトではありません) に作成され、そのプロジェクトに 2 つのファイル AppEventReceiver.svc ファイルとその分離コード AppEventReceiver.svc.cs ファイルが追加されます (これらのファイル名は "App" という文字列で始まりますが、これはアドインが以前「アプリ」と呼ばれていたためです。これらのファイル名は変更しないでください。Office Developer Tools for Visual Studio は、ファイルの名前がこのままになっていると想定しています)。
ハンドラーの URL は、アドイン マニフェストに登録されます。 マニフェストのこの部分は、マニフェスト デザイナーには表示されません。 これを確認するには、AppManifest.xml ファイルを右クリックして、[コードの表示] を選択します。 Properties 要素の新しい子は、次のようになります。
<InstalledEventEndpoint>~remoteAppUrl/Services/AppEventReceiver.svc</InstalledEventEndpoint>
このマークアップは、アドインのインストールに関連する独自の操作がすべて完了したときに、このサービスの ProcessEvent メソッドを呼び出すように SharePoint に通知します。 カスタム ハンドラーは、インストールの一環として最後に実行されます。 文字列
~remoteAppUrl
は、Office Developer Tools for Visual Studio によってサービスのホスト URL に置き換えられるプレースホルダーです。 デバッグ時には、Azure Service Bus の URL にします。 実稼働環境に展開するためのパッケージを作成するときには、実稼働環境の URL にします。
AppEventReceiver.svc.cs ファイルを開きます。
Office Developer Tools for Visual Studio によって作成された、ProcessEvent メソッドの実装例を確認できます。 このメソッドのすべての実装は SPRemoteEventResult オブジェクトを初期化することで始まり、そのオブジェクトを SharePoint に返すことで終了します。 このオブジェクトの主な役割は、カスタムの処理ロジックが失敗したために、イベントのロールバックが必要かどうかを SharePoint に通知することです。
ツールによって、この ClientContext オブジェクトを作成するメソッドに using ブロックが含まれていることもあります。 チェーン ストア アドインのカスタム ハンドラーは、SharePoint にコールバックすることがないため、このブロックは削除します。 メソッドは、次のようになります。
public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties) { SPRemoteEventResult result = new SPRemoteEventResult(); return result; }
イベント レシーバーは、次の 3 つのアドイン イベントによって呼び出される可能性があるため、ProcessEvent メソッド内の
result
オブジェクトを作成する行と取得する行の間に、次の switch 構造を追加します (イベント名には "App" という文字列が含まれますが、これは、アドインが以前「アプリ」と呼ばれていたためです)。switch (properties.EventType) { case SPRemoteEventType.AppInstalled: // TODO2: Custom installation logic goes here. break; case SPRemoteEventType.AppUpgraded: // This sample does not implement an add-in upgrade handler. break; case SPRemoteEventType.AppUninstalling: // TODO3: Custom uninstallation logic goes here. break; }
このインストール ロジックでは、香港店をテナントとしてリモート Web アプリケーションに登録するために、SQL ストアド プロシージャを呼び出すことになります。 特に重要なこととして、この処理が失敗したときには、ハンドラーがアドインのインストールをロールバックするように SharePoint に通知する必要があるため、
TODO2
の代わりに次の try/catch ブロックを追加します。try { CreateTenant(tenantName); } catch (Exception e) { // Tell SharePoint to cancel and roll back the event. result.ErrorMessage = e.Message; result.Status = SPRemoteEventServiceStatus.CancelWithError; }
このコードについては、次の点に注意してください。
tenantName
オブジェクトとCreateTenant
メソッドは、今後の手順で作成します。- SPRemoteEventResult オブジェクトの Status プロパティには、Continue (既定値)、CancelNoError、CancelWithError の 3 つの値を指定できます。 後者の 2 つのいずれかが、SharePoint にイベントをロールバックするように指示します。
サンプルのテナントの識別子になるホスト Web の URL は、SharePoint が SPRemoteEventProperties パラメーターでレシーバーに渡す情報の一部です。 ProcessEvent メソッドの SPRemoteEventResult オブジェクトの初期化部分の直後に次の行追加します。
string tenantName = properties.AppEventProperties.HostWebFullUrl.ToString();
この時点で、多少特殊な AppEventProperties.HostWebFullUrl プロパティをコードで処理する必要があります。 その他のほとんどのコンテキストでは、SharePoint はホスト Web URL の末尾に終了文字の
"/"
を含めるため、サンプル コードのロジックでは、この文字が存在することを想定しています。 ただし、SharePoint はホスト Web がサイト コレクションのルート Web の場合にのみ、この文字を HostWebFullUrl 値の末尾に追加します。 香港の Web サイトはサイト コレクションのサブ Web になるため、この文字を追加することでサンプル全体で同じテナント名文字列が使用されるようにする必要があります。tenantName
オブジェクトの初期化部分の下に次のコードを追加します。if (!tenantName.EndsWith("/")) { tenantName += "/"; }
次の using ステートメントをファイルの先頭に追加します。
using System.Data.SqlClient; using System.Data; using ChainStoreWeb.Utilities;
AppEventReceiver
クラスに、次のメソッドを追加します。 このシリーズ記事の目的は、SQL Server/Azure のプログラミングではなく、SharePoint アドインのプログラミングについて説明することなので、これについては詳しく説明しません。private void CreateTenant(string tenantName) { // Do not catch exceptions. Allow them to bubble up and trigger roll back // of installation. using (SqlConnection conn = SQLAzureUtilities.GetActiveSqlConnection()) using (SqlCommand cmd = conn.CreateCommand()) { conn.Open(); cmd.CommandText = "AddTenant"; cmd.CommandType = CommandType.StoredProcedure; SqlParameter name = cmd.Parameters.Add("@Name", SqlDbType.NVarChar); name.Value = tenantName; cmd.ExecuteNonQuery(); }//dispose conn and cmd }
このメソッドでは、Tenants というデータベース テーブルに行を作成します。 Name 列に加えて、このテーブルには既定値が 0000.0000.0000.0000 に設定された Version 列もあります。 このシリーズの今後の記事では、アドインがホスト Web に既にインストールされているかどうかを判断するために、この値を調べる初回実行ロジックを作成します。 バージョンが 0000.0000.0000.0000 の場合は、コードで [現地の従業員] リストと [出荷予定] リストを展開してから、バージョン番号を大きくします。
アンインストール ハンドラーを作成する
通常は、インストール イベントを処理するときには常にアンインストール イベントも処理するようにしてください。 基本的な考え方は、インストール ハンドラーが展開したものをアンインストール ハンドラーで削除するかごみ箱に移動します。 ただし、多数の例外があるため、アドインのユース ケースを実際に理解する必要があります。 たとえば、アドインでリストを展開して、そのリストにアドインで値を設定した場合、アドイン自体がアンインストールされた後でもリストには値が保持されていることがあります。この場合は、アンインストール イベント ハンドラーでリストを削除する必要はありません。
アンインストール イベントは、[サイト コンテンツ] ページからユーザーがアドインを削除したときには期待どおりに実行されません。 そのようにしても、Web サイトのごみ箱にアドインを移動するだけになります。 ユーザーはアドインを復元できますが、復元してもインストール イベント ハンドラーは再実行されないため、インストール イベント ハンドラーで展開したものはアドインが復元された場合はそのまま存在している必要があります。 SharePoint コンポーネントは、ごみ箱から第 2 段階のごみ箱に移動できます。 アドインが第 2 段階のごみ箱から削除されたときにのみアンインストール イベントが発生します。その操作をユーザーが実行すると、アドインの復元は不可能になるため、その時点で香港店のテナントを企業データベースから削除します。
[アドインのアンインストールの処理] の値を [True] に設定します (これは、まだ [アプリのハンドルをアンインストール中] になっている可能性もあります)。 これにより、前述の手順で登録したインストール ハンドラーと同じように、AppManifest.xml ファイルにハンドラーを登録します。 ファイルを調べると、それらの URL がまったく同じになっていることがわかります。 Office Developer Tools for Visual Studio では、同じ *.svc ファイルを使用していることを前提としています。 このサンプルでは、それを実行しています (これは標準的な方法です)。
AppEventReceiver.svc.cs ファイル内の
TODO3
の代わりに次のコードを追加します。try { DeleteTenant(tenantName); } catch (Exception e) { // Tell SharePoint to cancel and roll back the event. result.ErrorMessage = e.Message; result.Status = SPRemoteEventServiceStatus.CancelWithError; }
このコードについては、次の点に注意してください。
DeleteTenant
メソッドは、この次の手順で追加します。- アドインのアンインストールをロールバックすると、そのアドインは第 2 段階のごみ箱に残されます。このごみ箱からは、まだアドインを復元できます。
次のメソッドを
AppEventReceiver
クラスに追加します。private void DeleteTenant(string tenantName) { // Do not catch exceptions. Allow them to bubble up and trigger roll back // of un-installation (removal from 2nd level Recycle Bin). using (SqlConnection conn = SQLAzureUtilities.GetActiveSqlConnection()) using (SqlCommand cmd = conn.CreateCommand()) { conn.Open(); cmd.CommandText = "RemoveTenant"; cmd.CommandType = CommandType.StoredProcedure; SqlParameter name = cmd.Parameters.Add("@Name", SqlDbType.NVarChar); name.Value = tenantName; cmd.ExecuteNonQuery(); }//dispose conn and cmd }
注:
このシリーズの前の記事では、F5 キーを押すたびに企業のデータベースを再構築するようにプロジェクトを構成しました。 これにより、Tenants テーブルが空になります。
アドインを実行してインストール ハンドラーをテストする
F5 キーを使用して、アドインを展開して実行します。 Visual Studio は、IIS Express でリモート Web アプリケーションをホストして、SQL Express で SQL データベースをホストします。 また、テスト用 SharePoint サイトにアドインを一時的にインストールし、インストール イベント ハンドラーを実行して、すぐにアドインを実行します。 開始ページが表示される前に、アドインへのアクセス許可を付与するように求めるダイアログが表示されます。
アドインの開始ページが開いたら、上部にあるクロム コントロールの歯車アイコンを選択して、[アカウント設定] を選択します。
[アカウント設定] ページで、[アドインのバージョンの表示] ボタンをクリックします。 バージョンは、0000.0000.0000.0000 と表示されます。
図 1. [アカウント設定] ページ
デバッグ セッションを終了するには、ブラウザー ウィンドウを閉じるか、Visual Studio でデバッグを停止します。 F5 キーを選択するたびに、Visual Studio は以前のバージョンのアドインを取り消し、最新のバージョンをインストールします。
このアドインおよび他の記事の Visual Studio ソリューションを操作し、それが終了したら前回のアドインを取り消すとよいでしょう。 ソリューション エクスプローラーでプロジェクトを右クリックして、[取り消し] を選択します。
次の手順
このシリーズの次の記事では、[現地の従業員] リストとカスタム リボンのボタンをプログラムで展開するアドインに初回実行ロジックを追加します (「プロバイダー向けのホスト型アドインに初回実行時のロジックを追加する」)。