プロバイダー向けのホスト型アドインでリスト アイテム イベントを処理する

これは、プロバイダー ホスト型の SharePoint アドインの開発の基本に関する記事のシリーズの 10 番目です。SharePoint アドイン とこのシリーズの前の記事 (プロバイダー ホスト型の SharePoint アドインの作成を始めるにある記事) をよく理解しておいてください。

注:

プロバイダー ホスト型アドインに関するこのシリーズに沿って作業してきた場合は、このトピックでも引き続き使用できる Visual Studio ソリューションを既に所有しています。 また、SharePoint_Provider-hosted_Add-Ins_Tutorials でリポジトリをダウンロードして BeforeRER.sln ファイルを開くこともできます。

このシリーズの前の記事では、注文の発生時に、その注文は企業のデータベースの Orders テーブルに追加され、その注文に対応するアイテムは [出荷予定] リストに自動的に追加されるようになっていました。 そのアイテムが現地店舗に到着したときに、ユーザーは [配送済み] 列を [はい] に設定します。 アイテムのフィールド値を変更することで、アイテム更新イベントが発生します。カスタム ハンドラーは、このイベントに追加できます。

この記事では、このリスト アイテム イベントのハンドラーを作成して、SharePoint アドインの初回実行ロジックで、そのハンドラーをプログラムによって展開します。 このハンドラーによって、企業のデータベースの Inventory テーブルにアイテムを追加します。 その後で、[出荷予定] リストの [在庫に追加済み] リストを [はい] に設定します。 また、この 2 番目のアイテム更新イベントが、アイテム更新イベントを無限に繰り返さないようにする方法についても説明します。

[出荷予定] リストのプログラムによる展開

注:

Visual Studio のスタートアップ プロジェクトの設定は、ソリューションが開かれるたびに、既定値に戻される傾向があります。 このシリーズ記事のサンプル ソリューションを再開した直後は、次の手順を必ず実行してください。

  1. ソリューション エクスプローラーの上部にあるソリューション ノードを右クリックして、[スタートアップ プロジェクトの設定] を選択します。
  2. 3 つすべてのプロジェクトが [アクション] 列で [開始] に設定されていることを確認します。
  1. ソリューション エクスプローラーで、ChainStoreWeb プロジェクトの Utilities\SharePointComponentDeployer.cs ファイルを開きます。 SharePointComponentDeployer クラスに、次のメソッドを追加します。

       private static void CreateExpectedShipmentsList()
      {
         using (var clientContext = sPContext.CreateUserClientContextForSPHost())
         {
     	var query = from list in clientContext.Web.Lists
     		    where list.Title == "Expected Shipments"
     		    select list;
     	IEnumerable<List> matchingLists = clientContext.LoadQuery(query);
     	clientContext.ExecuteQuery();
    
     	if (matchingLists.Count() == 0)
     	{
     		ListCreationInformation listInfo = new ListCreationInformation();
     		listInfo.Title = "Expected Shipments";
     		listInfo.TemplateType = (int)ListTemplateType.GenericList;
     		listInfo.Url = "Lists/ExpectedShipments";
     		List expectedShipmentsList = clientContext.Web.Lists.Add(listInfo);
    
     		Field field = expectedShipmentsList.Fields.GetByInternalNameOrTitle("Title");
     		field.Title = "Product";
     		field.Update();
    
     		expectedShipmentsList.Fields.AddFieldAsXml("<Field DisplayName='Supplier'" 
     							    + " Type='Text' />", 
     							    true,
     							    AddFieldOptions.DefaultValue);
     		expectedShipmentsList.Fields.AddFieldAsXml("<Field DisplayName='Quantity'" 
     							    + " Type='Number'" 
     							    + " Required='TRUE' >" 
     							    + "<Default>1</Default></Field>",
     							    true, 
     							    AddFieldOptions.DefaultValue);
     		expectedShipmentsList.Fields.AddFieldAsXml("<Field DisplayName='Arrived'" 
     							   + " Type='Boolean'"
     							   + " ShowInNewForm='FALSE'>"
     							   + "<Default>FALSE</Default></Field>",
     							    true, 
     							    AddFieldOptions.DefaultValue);
     		expectedShipmentsList.Fields.AddFieldAsXml("<Field DisplayName='Added to Inventory'" 
     							    + " Type='Boolean'" 
     							    + " ShowInNewForm='FALSE'>"
     							    + "<Default>FALSE</Default></Field>", 
     							    true, 
     							    AddFieldOptions.DefaultValue);
    
     		clientContext.ExecuteQuery();
     	}
          }
      }
    

    このコードには、このシリーズの前の記事で示していない機能は導入されていませんが、次の点に注意してください。

    • [数量] フィールドの Required 属性が TRUE に設定されるので、このフィールドに必ず値を指定する必要があります。 その後、既定値 を 1 に設定します。

    • [新しいアイテム] フォームの [配送済み][在庫に追加済み] のフィールドが非表示になります。

    • [アイテムの編集] フォームの [在庫に追加済み] フィールドも非表示にすることが理想的です。このフィールドは、アイテム更新ハンドラーが、企業の Inventory テーブルに初めてアイテムを追加するときにのみ [はい] に変更する必要があるためです。 今後の手順で説明するように、技術的な理由から、イベント更新ハンドラーでプログラムによって [アイテムの編集] フォームのフィールドに書き込む場合、そのフィールドは表示しておく必要があります。

  2. DeployChainStoreComponentsToHostWeb メソッドで、行 RemoteTenantVersion = localTenantVersion の直前に、次の行を追加します。

      CreateExpectedShipmentsList();
    

リスト アイテム イベント レシーバーの作成

注:

このシリーズ記事に沿って作業を進めてきた場合は、リモート イベント レシーバーのデバッグ用開発環境が既に構成されています。 その作業が完了していない場合は、「イベント レシーバーのデバッグ用のソリューションを構成する」を参照してから、このトピックを進めてください。

Office Developer Tools for Visual Studio には、SharePoint アドイン ソリューションに追加できる、[リモート イベント レシーバー] という項目が含まれています。 ただし、この記事の執筆時には、このプロジェクト項目はリスト (レシーバーを登録するリスト) がアドイン Web に存在していることを前提としているため、このツールによってアドイン Web が作成されて、そこに SharePoint 成果物が作成されます。 このチェーン ストア アドインのレシーバーは、今後の手順でホスト Web 上の [出荷予定] リストに登録するため、このアドインにアドイン Web は不要です (アドイン Web とホスト Web の相違点については、「SharePoint アドイン」を参照してください)。

注:

リストとリスト アイテムのイベント レシーバーはリモート イベント レシーバー (RER) と呼ばれます。コードは SharePoint からリモートであるため、クラウドまたは SharePoint ファーム外のオンプレミス サーバーにあります。 ただし、それらをトリガーするイベントは SharePoint にあります。

  1. ソリューション エクスプローラーで、ChainStoreWeb プロジェクト内の [サービス] フォルダーを右クリックして、[追加]>[WCF サービス] の順に選択します。

  2. ダイアログが表示されたら、サービスに「RemoteEventReceiver1」という名前を付けて、[OK] をクリックします。

  3. ツールは、インターフェイス ファイル、*.svc ファイル、および分離コード ファイルを作成します。 インターフェイス ファイルの IRemoteEventReceiver1.cs は不要なので削除します (このファイルは、ツールによって自動的に開かれることがあります。その場合は、ファイルを閉じてから削除してください)。

    注:

    このシリーズの前の記事で、インストール イベントとアンインストール イベントのアドイン イベント レシーバーを作成したときに、それらの URL は Office Developer Tools for Visual Studio によってアプリ マニフェスト ファイルに追加されています。 リストとリスト アイテム イベント レシーバーは、アプリ マニフェストに登録されていません。 その代わりに、プログラムによって (プロバイダー向けのホスト型アドイン) に登録します。 その操作は、この後の手順で実行します。

  4. 分離コードファイル RemoteEventReceiver1.svc.cs を開きます。 すべてのコンテンツを次のコードに置き換えます。

       using System;
     using System.Collections.Generic;
     using Microsoft.SharePoint.Client;
     using Microsoft.SharePoint.Client.EventReceivers;
     using System.Data.SqlClient;
     using System.Data;
     using ChainStoreWeb.Utilities;
    
     namespace ChainStoreWeb.Services
     {
         public class RemoteEventReceiver1 : IRemoteEventService
         {
     	/// <summary>
     	/// Handles events that occur before an action occurs, 
     	/// such as when a user is adding or deleting a list item.
     	/// </summary>
     	/// <param name="properties">Holds information about the remote event.</param>
     	/// <returns>Holds information returned from the remote event.</returns>
     	public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
     	{
     	    throw new NotImplementedException();
     	}
    
     	/// <summary>
     	/// Handles events that occur after an action occurs, 
     	/// such as after a user adds an item to a list or deletes an item from a list.
     	/// </summary>
     	/// <param name="properties">Holds information about the remote event.</param>
     	public void ProcessOneWayEvent(SPRemoteEventProperties properties)
     	{
    
     	}
         }
     }
    

    このコードについては、次の点に注意してください。

    • インターフェイス IRemoteEventService は、Microsoft.SharePoint.Client.EventReceivers 名前空間内で定義されています。

    • チェーン ストア アドインで処理される "before" イベントはありませんが、IRemoteEventService インターフェイスには ProcessEvent メソッドが必要になります。

  5. 次のコードを ProcessOneWayEvent メソッドに追加します。 このサンプルが処理するイベントは ItemUpdated イベントだけなので、これまで単純な if 構造を switch の代わりに使用してきたことに留意してください。 しかし、通常イベント レシーバーは複数のイベントを処理するので、ここでは SharePoint アドイン開発者がイベント ハンドラーで最もよく使用するパターンを確認します。

       switch (properties.EventType)
     {
         case SPRemoteEventType.ItemUpdated:
    
     	// TODO12: Handle the item updated event.
    
     	break;
     }  
    
  6. TODO12 を次のコードに置き換えます。 ここでも SharePoint イベント レシーバーの一般的なパターンを理解できるようにするために、単純な if 構造ではなく switch 構造を使用しています。

       switch (properties.ItemEventProperties.ListTitle)
     {
         case "Expected Shipments":
    
     	// TODO13: Handle the arrival of a shipment.
    
     	break;
     }
    
  7. 出荷品の到着に応答するコードには、次の 2 つの処理が必要です。

    • ストアに届いたアイテムを、企業の在庫に追加します。

    • [出荷予定] リスト上の [在庫に追加済み] フィールドを [はい] に設定します。 しかし、この操作はアイテムが在庫に追加された場合のみ必要です。

    TODO13 の代わりに次のコードを追加します。 2 つのメソッド TryUpdateInventoryRecordInventoryUpdateLocally は、この後の手順で作成します。

       bool updateComplete = TryUpdateInventory(properties);
     if (updateComplete)
     {
         RecordInventoryUpdateLocally(properties);
     }
    
  8. ProcessOneWayEvent メソッドは、現時点で次のようになります。

       public void ProcessOneWayEvent(SPRemoteEventProperties properties)
     {
         switch (properties.EventType)
         {
     	case SPRemoteEventType.ItemUpdated:
    
     	    switch (properties.ItemEventProperties.ListTitle)
     	    {
     		case "Expected Shipments":
     		    bool updateComplete = UpdateInventory(properties);
     		    if (updateComplete)
     		    {
     			RecordInventoryUpdateLocally(properties);
     		    }
     		    break;
     	    }
     	    break;
         }          
     }
    
  9. RemoteEventReceiver1 クラスに、次のメソッドを追加します。

       private bool TryUpdateInventory(SPRemoteEventProperties properties)
     {
         bool successFlag = false;
    
     	// TODO14: Test whether the list item is changing because the product has arrived
     	// or for some other reason. If the former, add it to the inventory and set the success flag
     	// to true.     
    
         return successFlag;
     }
    
  10. [出荷予定] リストには 5 つの列がありますが、アイテムに対するほとんどの種類の更新にハンドラーが反応することは望ましくありません。 たとえば、ユーザーがサプライヤーの名前の間違いを修正すると、アイテム更新イベントがトリガーされますが、このハンドラーでは何もする必要がありません。 このハンドラーは、[配送済み] フィールドが [はい] に設定された直後にのみ反応する必要があります。

    その他にもテストが必要な条件があります。 [配送済み][はい] に設定され、アイテム内の製品が在庫に追加された ([在庫に追加済み][はい] に設定された) とします。 このとき、ユーザーが間違って出荷の [配送済み] フィールドを [いいえ] に戻してしまい、その間違いを修正するために再度 [はい] に設定したとします。 間違いと修正の両方で、アイテム更新イベントがトリガーされます。 このハンドラーは間違いには反応しません。ハンドラーは [配送済み][はい] になったときにのみ動作するためです。ただし、[配送済み][はい] に戻す修正には反応します。そのため、同じ製品と数量が在庫に 2 回追加されることになります。 こうした理由から、ハンドラーは [在庫に追加済み] の値が [いいえ] のときにのみ動作する必要があります。

    そのため、ハンドラーでは、ユーザーがアイテムを更新した直後に、これらのフィールドの値の内容を知る必要があります。 SPRemoteEventProperties オブジェクトには、ItemEventProperties プロパティがあります。 また、インデックス付きの AfterProperties プロパティもあります。このプロパティは、更新済みアイテムのフィールドの値を保持しています。 次に示すコードでは、これらのプロパティを使用して、ハンドラーが反応する必要があるかどうかをテストします。 TODO14 の代わりに、このコードを使用します。

     var arrived = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Arrived"]);
    var addedToInventory = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Added_x0020_to_x0020_Inventory"]);
    
    if (arrived && !addedToInventory)
    {
    
       // TODO15: Add the item to inventory
    
       successFlag = true;
    }
    
  11. TODO15 を次のコードに置き換えます。

     using (SqlConnection conn = SQLAzureUtilities.GetActiveSqlConnection())
    using (SqlCommand cmd = conn.CreateCommand())
    {
       conn.Open();
       cmd.CommandText = "UpdateInventory";
       cmd.CommandType = CommandType.StoredProcedure;
       SqlParameter tenant = cmd.Parameters.Add("@Tenant", SqlDbType.NVarChar);
       tenant.Value = properties.ItemEventProperties.WebUrl + "/";
       SqlParameter product = cmd.Parameters.Add("@ItemName", SqlDbType.NVarChar, 50);
       product.Value = properties.ItemEventProperties.AfterProperties["Title"]; // not "Product"
       SqlParameter quantity = cmd.Parameters.Add("@Quantity", SqlDbType.SmallInt);
       quantity.Value = Convert.ToUInt16(properties.ItemEventProperties.AfterProperties["Quantity"]);
       cmd.ExecuteNonQuery();
    }
    

    これは、主に SQL と ASP.NET のプログラミングになるため詳しく説明しませんが、次の点に注意してください。

    • ItemEventProperties.WebUrl プロパティを使用してテナント名を取得します。テナント名は、ホスト Web の URL です。

    • もう一度 AfterProperties を使用して、製品の名前と数量の値を取得します。

    • フィールドは常に内部名によって参照されるため、表示名が "Product" ( CreateExpectedShipmentsList メソッド内) に変更された場合でも、製品名フィールドは "Title" と呼ばれます。

  12. TryUpdateInventory メソッドは、まだ完成していませんが、この時点では次のようになります。

     private bool TryUpdateInventory(SPRemoteEventProperties properties)
    {
       bool successFlag = false;
    
       var arrived = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Arrived"]);
       var addedToInventory = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Added_x0020_to_x0020_Inventory"]);
    
       if (arrived && !addedToInventory)
       {
    	using (SqlConnection conn = SQLAzureUtilities.GetActiveSqlConnection())
    	using (SqlCommand cmd = conn.CreateCommand())
    	{
    	    conn.Open();
    	    cmd.CommandText = "UpdateInventory";
    	    cmd.CommandType = CommandType.StoredProcedure;
    	    SqlParameter tenant = cmd.Parameters.Add("@Tenant", SqlDbType.NVarChar);
    	    tenant.Value = properties.ItemEventProperties.WebUrl + "/";
    	    SqlParameter product = cmd.Parameters.Add("@ItemName", SqlDbType.NVarChar, 50);
    	    product.Value = properties.ItemEventProperties.AfterProperties["Title"]; // not "Product"
    	    SqlParameter quantity = cmd.Parameters.Add("@Quantity", SqlDbType.SmallInt);
    	    quantity.Value = Convert.ToUInt16(properties.ItemEventProperties.AfterProperties["Quantity"]);
    	    cmd.ExecuteNonQuery();
    	}            
    	successFlag = true;
       }  
       return successFlag;
    }
    
  13. TryUpdateInventory メソッドが true を返すと、ハンドラーは [在庫に追加済み] フィールドを [はい] に設定することで [出荷予定] リスト内の同じアイテムを更新するメソッド (まだ作成していません) を呼び出します。 これ自体がアイテム更新イベントであるため、ハンドラーが再度呼び出されます ([在庫に追加済み] フィールドが [はい] になっていることで、ハンドラーは在庫に同じ出荷を 2 回追加しないようになりますが、ハンドラーが呼び出されることに変わりはありません)。

    SharePoint は、プログラムによる更新によってアイテム更新イベントがトリガーされると、動作が少し異なります。更新プログラムで変更されたフィールドは AfterProperties にのみ含まれます。[在庫に追加] フィールドのみが変更されたため、[到着] フィールドは表示されません。

    次の行

    var arrived = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Arrived"]);

    は、KeyNotFoundException をスローします。

    この問題を解決する方法は複数あります。 このサンプルでは、例外をキャッチし、 catch ブロックを使用して、確実に successFlagfalse に設定されるようにします。 こうすると、アイテムが 3 回更新されることはありません。

    メソッド内の最初の行 bool successFlag = false; と最後の行 return successFlag; の間にあるものをすべて try ブロック内に組み込みます。

  14. この try ブロックのすぐ下に、次の catch ブロックを追加します。

     catch (KeyNotFoundException)
    {
       successFlag = false;
    }
    

    注:

    [アイテムの編集] フォームで [在庫に追加済み] フィールドを表示したままにしておく必要がある別の理由は KeyNotFoundException です。 SharePoint は、[アイテムの編集] フォームで非表示になっているフィールドを AfterProperties に組み込みません。

  15. この時点で、メソッド全体は次のようになります。

     private bool TryUpdateInventory(SPRemoteEventProperties properties)
    {
       bool successFlag = false;
    
       try 
       {
    	var arrived = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Arrived"]);
    	var addedToInventory = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Added_x0020_to_x0020_Inventory"]);
    
    	if (arrived && !addedToInventory)
    	{
    	    using (SqlConnection conn = SQLAzureUtilities.GetActiveSqlConnection())
    	    using (SqlCommand cmd = conn.CreateCommand())
    	    {
    		conn.Open();
    		cmd.CommandText = "UpdateInventory";
    		cmd.CommandType = CommandType.StoredProcedure;
    		SqlParameter tenant = cmd.Parameters.Add("@Tenant", SqlDbType.NVarChar);
    		tenant.Value = properties.ItemEventProperties.WebUrl + "/";
    		SqlParameter product = cmd.Parameters.Add("@ItemName", SqlDbType.NVarChar, 50);
    		product.Value = properties.ItemEventProperties.AfterProperties["Title"]; // not "Product"
    		SqlParameter quantity = cmd.Parameters.Add("@Quantity", SqlDbType.SmallInt);
    		quantity.Value = Convert.ToUInt16(properties.ItemEventProperties.AfterProperties["Quantity"]);
    		cmd.ExecuteNonQuery();
    	    }            
    	    successFlag = true;
    	}  
       }
       catch (KeyNotFoundException)
       {
    	successFlag = false;
       }
       return successFlag;
    }
    
  16. RemoteEventReceiver1 クラスに、次のメソッドを追加します。

     private void RecordInventoryUpdateLocally(SPRemoteEventProperties properties)
    {
       using (ClientContext clientContext = TokenHelper.CreateRemoteEventReceiverClientContext(properties))
       {
    	List expectedShipmentslist = clientContext.Web.Lists.GetByTitle(properties.ItemEventProperties.ListTitle);
    	ListItem arrivedItem = expectedShipmentslist.GetItemById(properties.ItemEventProperties.ListItemId);
    	arrivedItem["Added_x0020_to_x0020_Inventory"] = true;
    	arrivedItem.Update();
    	clientContext.ExecuteQuery();
       }
    }
    

    このコードのパターンについても、このシリーズのこれまでの記事から比べると慣れてきたころでしょう。 ただし、1 つの違いがあります。

    • このコードでは、EmployeeAdder ページなどのページから SharePoint 内に呼び出されたコードで使用した SharePointContext.CreateUserClientContextForSPHost メソッドの代わりに TokenHelper.CreateRemoteEventReceiverClientContext メソッドを呼び出すことで ClientContext オブジェクトを取得します。

    • ClientContext オブジェクトの取得のために異なるメソッドを用意した主な理由は、そうしたオブジェクトを作成するために SharePoint が必要とする情報をイベント レシーバーに渡す方法と、ページに渡す方法が異なるためです。 イベント レシーバーの場合は SPRemoteEventProperties を渡しますが、ページの場合はコンテキスト トークンと呼ばれる特殊なフィールドを渡します。このトークンは、アドイン ページを起動する要求の本文で渡します。

  17. レシーバーのコード ファイルを保存して閉じます。

レシーバーの登録

最後のタスクは、 [出荷予定] リストのアイテムが更新されるたびに、あるカスタム レシーバーを SharePoint で呼び出すように、SharePoint に指示することです。

  1. SharePointContentDeployer.cs ファイルを開いて、DeployChainStoreComponentsToHostWeb メソッドの [出荷予定] リストを作成する行の直後に次の行を追加します (このメソッドは、この後の手順で追加します)。 このメソッドには、アドインの開始ページで DeployChainStoreComponentsToHostWeb メソッドに渡した HttpRequest オブジェクトを渡している点に注意してください。

      RegisterExpectedShipmentsEventHandler(request);
    
  2. SharePointComponentDeployer クラスに、次のメソッドを追加します。

       private static void RegisterExpectedShipmentsEventHandler(HttpRequest request)
     {
         using (var clientContext = sPContext.CreateUserClientContextForSPHost())    
         {
     	var query = from list in clientContext.Web.Lists
     		    where list.Title == "Expected Shipments"
     		    select list;
     	IEnumerable<List> matchingLists = clientContext.LoadQuery(query);
     	clientContext.ExecuteQuery();
    
     	List expectedShipmentsList = matchingLists.Single();
    
     	// TODO16: Add the event receiver to the list's collection of event receivers.       
    
     	clientContext.ExecuteQuery();
         }
     }
    
  3. TODO16 を次の行に置き換えます。 リストとリスト アイテムの場合と同様に、イベント レシーバーにも軽量の CreationInformation クラスがある点に注意してください。

     EventReceiverDefinitionCreationInformation receiver = new EventReceiverDefinitionCreationInformation();
     receiver.ReceiverName = "ExpectedShipmentsItemUpdated";
     receiver.EventType = EventReceiverType.ItemUpdated;
    
      // TODO17: Set the URL of the receiver.
    
     expectedShipmentsList.EventReceivers.Add(receiver);
    
    
  4. この時点で、SharePoint にイベント レシーバーの URL を通知する必要があります。 実稼働環境では、リモート ページと同じドメインにして、パスは /Services/RemoteEventReceiver1.svc にします。 ハンドラーはアドインの開始ページから初回実行時ロジックで登録されるため、このドメインはページを呼び出した要求の HttpRequest オブジェクトのホスト ヘッダー内にあります。 このコードでは、そのオブジェクトをページから DeployChainStoreComponentsToHostWeb メソッドに渡しています。このメソッド自体はオブジェクトを RegisterExpectedShipmentsEventHandler メソッドに渡しています。 そのため、レシーバーの URL は次のコードで設定できます。

    receiver.ReceiverUrl = "https://" + request.Headers["Host"] + "/Services/RemoteEventReceiver1.svc";

    残念ながら、これは、Visual Studio からアドインをデバッグしているときには動作しません。 デバッグ中は、レシーバーが Azure Service Bus でホストされています。リモート ページがホストされている localhost の URL ではありません。 デバッグ中かどうかに応じて異なるレシーバーの URL を設定する必要があるため、TODO17 を次の構造に置き換えて C# コンパイラのディレクティブを使用するようにします。 デバッグ モードでは、レシーバーの URL が web.config 設定から読み取られる点に注意してください (この設定は、この後の手順で作成します)。

       #if DEBUG
     		    receiver.ReceiverUrl = WebConfigurationManager.AppSettings["RERdebuggingServiceBusUrl"].ToString();
     #else
     		    receiver.ReceiverUrl = "https://" + request.Headers["Host"] + "/Services/RemoteEventReceiver1.svc"; 
     #endif
    
    
  5. この時点で、RegisterExpectedShipmentsEventHandler メソッド全体は次のようになります。

       private static void RegisterExpectedShipmentsEventHandler(HttpRequest request)
     {    
         using (var clientContext = sPContext.CreateUserClientContextForSPHost())
         {
     	var query = from list in clientContext.Web.Lists
     			    where list.Title == "Expected Shipments"
     			    select list;
     	IEnumerable<List> matchingLists = clientContext.LoadQuery(query);
     	clientContext.ExecuteQuery();
    
     	List expectedShipmentsList = matchingLists.Single();
    
     	EventReceiverDefinitionCreationInformation receiver = new EventReceiverDefinitionCreationInformation();
     	receiver.ReceiverName = "ExpectedShipmentsItemUpdated";
     	receiver.EventType = EventReceiverType.ItemUpdated;
    
     #if DEBUG
     	receiver.ReceiverUrl = WebConfigurationManager.AppSettings["RERdebuggingServiceBusUrl"].ToString();
     #else
     	receiver.ReceiverUrl = "https://" + request.Headers["Host"] + "/Services/RemoteEventReceiver1.svc"; 
     #endif
     	expectedShipmentsList.EventReceivers.Add(receiver);
     	clientContext.ExecuteQuery();
         }
     }
    
  6. 次の using ステートメントをファイルの一番上に追加します。

      using System.Web.Configuration;
    
  7. アドインのデバッグ中にのみ DEBUG が true になっていることを確認するために、次のサブプロシージャを実行します。

    1. ソリューション エクスプローラーChainStoreWeb プロジェクトを右クリックして、[プロパティ] を選択します。

    2. [プロパティ][ビルド] タブを開いて、上部の [構成] ドロップダウンから [デバッグ] を選択します。

    3. [DEBUG 定数の定義] チェック ボックスがオンになっていることを確認します (通常は既定でオンになっています)。 次に示すスクリーンショットは適切な設定を示しています。

      図 1. Visual Studio の [プロパティ] タブの [ビルド] サブタブ

      Visual Studio の [プロパティ] タブの [ビルド] サブタブ。[構成] ドロップダウンが [デバッグ] に設定されています。[デバッグ定数の定義] のチェック ボックスがオンになっています。

    4. [構成] ドロップダウンを [リリース] に変更して、[DEBUG 定数の定義] チェック ボックスがオンになっていないことを確認します (通常は既定でオンになっています)。次のスクリーンショットは、適切な設定を示しています。 次に示すスクリーンショットは、適切な設定を示しています。

      図 2. チェック ボックスがオフの [プロパティ] タブの [ビルド] サブタブ

      [プロパティ] タブ内の [ビルド] サブタブ。[構成] ドロップ ダウンでは [リリース] が選択されています。[DEBUG 定数の定義] チェック ボックスはチェックされていません。

    5. 変更を加えた場合は、保存してから [プロパティ] タブを閉じます。

  8. web.config ファイルを開いて、appSettings 要素の子として次のマークアップを追加します (この設定の値は次のセクションで取得します)。

      <add key="RERdebuggingServiceBusUrl" value="" />
    

デバッグ用のレシーバー URL の取得

アドイン イベントとリスト アイテム イベントのレシーバーは、WCF (Windows Communication Service) サービスです。すべての WCF サービスは独自のエンドポイントを認識し、そのエンドポイントを System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri オブジェクトを含む複数の場所に格納します。

デバッグ中に、アドイン レシーバーは Azure Service Bus エンドポイントでホストされます。このエンドポイントはリスト アイテム レシーバーのエンドポイントとほとんど同じです。 違いは、アドイン エンドポイントの URL は "AppEventReceiver.svc" で終わりますが、リスト アイテム レシーバーの URL は "RemoteEventReceiver1.svc" で終わるということです。そのため、アドイン レシーバーでエンドポイントの URL を取得し、その末尾に小さな変更を加えてから、web.config RERdebuggingServiceBusUrl 設定の値として使用できます。

  1. ChainStoreWeb プロジェクトの [サービス] フォルダーで、AppEventReceiver.svc.cs ファイルを開きます。

  2. ProcessEvent メソッド内の最初の行として以下を追加します。

      string debugEndpoint = System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri.ToString(); 
    
  3. メソッドの次の行にブレークポイントを追加します。

  4. F5 キーを押してアドインをデバッグします。 F5 キーを押すたびに、web.config が開いていて Office Developer Tools for Visual Studio によって設定が変更されるため、再読み込みを求めるダイアログが表示されます。 [はい] を選択します。

  5. ブレークポイントにヒットしたら、debugEndpoint 変数の上にカーソルを重ねます。 Visual Studio データのヒント ウィンドウが表示されたら、下矢印をクリックして [テキスト ビジュアライザー] を選択します。

    図 3. Azure Service Bus URL を持つ Visual Studio テキスト ビジュアライザー

    Azure Service Bus URL が表示されている Visual Studio テキスト ビジュアライザー。

  6. ビジュアライザーから文字列の値をコピーし、任意の場所に貼り付けます。

  7. ビジュアライザーを閉じてから、Visual Studio でデバッグを停止します。

  8. このプロシージャの 2 番目のステップで追加した行を削除するかコメントにしてから、ブレークポイントも削除します。

  9. コピーした文字列で、末尾の "AppEventReceiver.svc" を "RemoteEventReceiver1.svc" に置き換えます。

  10. 変更した URL をコピーして、web.config ファイル内に RERdebuggingServiceBusUrl キーの値として貼り付けます。

注:

実稼働環境で実行中のリモート イベント レシーバーをデバッグするときに、サービス バスの URL を手動でコピーして、URL (変更したバージョンの URL) を web.config 内に貼り付ける以外にも、別の URL を使用する必要性に対応する方法があります。 System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri の値を SharePoint の任意の場所かリモート データベース内にプログラムによって保存してから、初回実行時のコードでこの値を読み取って receiver.ReceiverUrl プロパティに割り当てることもできます。 リスト アイテム イベント レシーバーは、アドイン インストール イベントのハンドラーの一部として登録することもできます。 そのようにすると、プログラムによって System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri を読み取って、変更してから receiver.ReceiverUrl に割り当てることで保存の必要がなくなります。

この戦略では、アドイン インストール イベントのハンドラーで [出荷予定] リストも作成する必要があります。このリストは、ハンドラーの登録前に存在している必要があるからです。

また、アドイン イベント レシーバーとリスト アイテム イベント レシーバーは、単一のレシーバーに組み合わせることもできます (つまり、同じ .svc ファイルと .svc.cs ファイルにする)。 その場合、receiver.ReceiverUrl の値として使用する前に、URL を変更する必要がなくなります。

アドインの実行とリスト アイテム レシーバーのテスト

  1. 香港店の Web サイトの [サイト コンテンツ] ページを開いて、[出荷予定] リストがある場合は削除します。

  2. F5 キーを使用して、アドインを展開して実行します。 Visual Studio は、IIS Express でリモート Web アプリケーションをホストして、SQL Express で SQL データベースをホストします。 また、テスト用 SharePoint サイトにアドインを一時的にインストールして、すぐにアドインを実行します。 開始ページが表示される前に、アドインへのアクセス許可を付与するように求めるダイアログが表示されます。

  3. アドインの開始ページが開いたら、上部のクロム コントロールにある [サイトに戻る] ボタンをクリックします。

  4. 香港店のホーム ページから、[サイト コンテンツ] ページに移動して、[出荷予定] リストを開きます。

  5. アイテムを作成して、新しいアイテムのフォームに [配送済み] フィールドと [在庫に追加済み] フィールドが表示されていないことを確認します。

  6. アイテムを作成したら、もう一度開いて編集します。 [配送済み] チェック ボックスをオンにして、アイテムを保存します。 これにより、アイテム更新イベントがトリガーされます。 アイテムは、在庫に追加されて [在庫に追加済み] フィールドの値が [はい] に変更されます ([在庫に追加済み] の変更を確認するにはページを最新の情報に更新することが必要になる場合があります)。

  7. ブラウザーの [戻る] ボタンを使用して、チェーン ストア アドインの開始ページに戻って、[在庫の表示] を選択します。 [配送済み] のマークを付けたアイテムがリストに表示されます。

  8. [出荷予定] リストに戻って、製品名とサプライヤーの名前は同じまま、数量が異なる別のアイテムを追加します。

  9. アイテムを作成したら、もう一度開いて編集します。 [配送済み] の値を [はい] に変更して、アイテムを保存します。

  10. ブラウザーの [戻る] ボタンを使用して、チェーン ストア アドインの開始ページに戻って、[在庫の表示] を選択します。 製品名とサプライヤーのアイテムは依然として 1 つだけですが、数量は [出荷予定] リストの 2 つのアイテムの合計になります。

  11. デバッグ セッションを終了するには、ブラウザー ウィンドウを閉じるか、Visual Studio でデバッグを停止します。 F5 キーを押すたびに、Visual Studio によって、以前のバージョンのアドインが取り消され、最新のバージョンがインストールされます。

  12. このアドインおよび他の記事の Visual Studio ソリューションを操作し、それが終了したら前回のアドインを取り消すとよいでしょう。 ソリューション エクスプローラーでプロジェクトを右クリックして、[取り消し] を選択します。

次の手順

SharePoint サイトにアドインを発行する方法については、「SharePoint アドインの展開とインストール: 方法とオプション」を参照してください。 次に示す各ページで、高度な SharePoint アドイン開発の作業を進めることもできます。