はじめに

前回記事では、4種類のストレージについての概要説明と、BLOB、キュー、ファイルストレージを操作する方法について簡単なサンプルとともに紹介しました。複雑な処理を行うための仕組みが簡単に実現できるということを感じていただけたでしょうか?

前回に引き続き、本記事ではWebアプリケーションを例題としてテーブルストレージを操作する方法について紹介します。

本連載では次の環境で動作確認を行っています。

・Windows 10 Pro
・Visual Studio Community 2015 Update3(以降、VS2015)
・Microsoft Azure SDK for .NET(VS 2015) 2.9.6

本稿で説明するリストはサンプルの中に収録しています。ご活用ください。

テーブルストレージを利用したアドレス帳Webアプリケーション

今回は、テーブルストレージデータを保存するWebアプリケーションを作成することを通じて、テーブルストレージへアクセスするための基本的な方法を説明します。紹介するサンプルでは次のように、登録フォームにて入力したアドレスをテーブルストレージに保存する画面、登録されたすべてのアドレス情報を閲覧できる画面などを実装します。

登録画面

一覧画面

なおデータの更新や削除、そして複雑な条件での検索などについて次回で紹介する予定です。サンプルプロジェクトは第1回目の記事と同様に、ASP.NET MVCにて作成しています。プロジェクト作成時の詳細な設定情報などはそちらをご覧ください。

実装するクラス、ファイル

このサンプルで説明するクラスやファイルは次の通りです。

今回紹介するファイル

ファイル 概要
Models\AddressEntity.cs アドレス帳データエンティティ
Models\StorageAddressBook.cs ストレージへのアクセス方法などを実装
Controllers\HomeController.cs コントローラ
Models\AddressViewModel.cs Viewとのやり取りに使用するViewModel
Views\Home\Index.cshtml 一覧表示用テンプレート
Views\Home\Add.cshtml 登録フォーム用テンプレート
Web.config ストレージへの接続設定
前回紹介したBLOB、キュー、ファイルストレージを操作するには、それぞれを操作するためのクライアントと対象となるクラスが用意されていました。テーブルストレージも同様に、クライアントとしてCloudTableClientクラス、対象となるテーブルを表すCloudTableクラスが用意されています。前回のサンプルではプログラム中で直接これらのクラスを操作していましたが、本記事のサンプルではMVCのコントローラとCloudTableの間に操作ロジックを取りまとめるためのクラスを用意しました(StorageAddressBook.cs)。ストレージテーブルに対する詳細処理をコントローラからは隠ぺいすることで、ユニットテストや将来への拡張に対する柔軟性を確保するようにしています。

データの流れ

また設定ファイルへの記述によって、データの保存先をローカルのPC(開発環境)にするのか、Azure(本番環境)にするのかを切り替えることができます。

それでは、各ファイルの内容を確認していきましょう。

データを扱うクラス

最初にテーブルストレージに保存するために必要となるクラスを紹介します。データそのものを扱うエンティティクラスと、テーブルストレージへのロジックを実装したクラスです。

1件のデータを扱うエンティティクラス

テーブルストレージで扱うデータはすべてTableEntityを継承したクラスとして定義する必要があります。次のクラスはメールアドレス、姓、名を扱うためのエンティティクラスです。

AddressEntity.cs(抜粋)

 // (1)エンティティはTableEntityを継承する
 public class AddressEntity : TableEntity
 {
   // <中略>
   // (2)Emailプロパティ
   public string Email
   {
     get { return this.RowKey + "@" + this.PartitionKey; }
     set
     {
       string[] strings = value.Split('@');
       this.RowKey = strings[0];
       this.PartitionKey = strings[1];
     }
   }
   // (3)LastName、FirstNameプロパティ
   public string LastName { get; set; }
   public string FirstName { get; set; }
 }

テーブルストレージで扱うデータはすべてTableEntityクラスから継承している必要があります(1)。上記の例ではメールアドレス、姓、名の値をそれぞれ、Email、LastName、FirstNameという名前でstring型プロパティとして定義しています。

テーブルストレージの場合には、必ず指定すべきプロパティとしてPartitionKeyとRowKeyが存在しますが、今回はPartitionKeyとしてメールアドレスのドメイン名、RowKeyにはローカル部(@の前の部分)を使用することにします(2)。このためEmailプロパティのsetterとgetterでは@で分割した値をそれぞれのキーに振り分けたり、結合したりしています。テーブルストレージでは、この2つのキーの組み合わせでユニークにする必要があるため、何を選ぶかは慎重に検討してください。

この2つ以外のプロパティは自由に設定できますので、他のプロパティも定義しておきましょう(3)。

テーブルストレージへのロジックを実装したクラス

次はテーブルストレージを操作するためのロジックを実装したクラスです。ストレージに対してアドレスを操作するという意味を込めてStorageAddressBookという名前にしました。

StorageAddressBook.cs(抜粋)

 public class StorageAddressBook
 {
   // (1)CloudTableインスタンスへの参照
   private CloudTable table;
   public StorageAddressBook()
   {
     // (2)Web.configに指定した接続情報を取得
     CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
       CloudConfigurationManager.GetSetting("StorageConnectionString"));
     // (3)テーブルクライアントの作成
     CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
     // (4)テーブルへの参照を取得し、存在しない場合には作成する
     table = tableClient.GetTableReference("address");
     table.CreateIfNotExists();
   }
 }

テーブルストレージの参照となるCloudTableは1度のリクエストで何回もアクセスする可能性があるため、フィールドとして保持しておきます(1)。CloudTableへの参照を取得するためには、コンストラクタにて次の操作を行っています。

他のストレージと同様に、最初に設定ファイルに記述した接続情報を取得し(2)、取得したCloudStorageAccountのインスタンスからテーブルクライアントを作成します(3)。そしてテーブル名を指定し、対象となるテーブルへの参照を取得します(4)。この例ではaddressという名前にしています。

データを登録する

これでテーブルストレージを操作する準備ができました。次はデータを登録する処理を見ていきます。

ストレージにデータを登録する

テーブルストレージへのデータ操作はすべてテーブルオペレーションクラスを使用して実行します。挿入、削除、更新などの目的ごとに用意されたTableOperationのスタティックメソッドに対してエンティティなどを渡して対応するオペレーションのインスタンスを作成し、CloudTable#Execute()に渡して実行します。

StorageAddressBook.cs(抜粋)

 public AddressEntity Add(AddressEntity entity)
 {
   // (1)挿入オペレーションを作成し、実行
   TableOperation insertOperation = TableOperation.Insert(entity);
   TableResult result = table.Execute(insertOperation);
   // (2)挿入されたエンティティを返す
   return (AddressEntity)result.Result;
 }

エンティティの登録を行うには、TableOperation#Insertを使用して挿入オペレーションを作成し、CloudTable#Execute()に渡してやります(1)。Executeを実行した結果としてTableResultが返ってくるので、このResultフィールドに設定されたエンティティを返します(2)。挿入結果のエンティティには更新日時などのシステムが付加する情報も含まれています。

エラー情報などをまとめて扱うビューモデル

登録処理を行う際には、例えば一意制約違反などでエラーになる場合がありますが、エンティティなどのモデル情報以外にエラー情報をビューで扱えるようにするため、複数のデータをまとめるビューモデルを定義します。

AddressViewModel.cs(抜粋)

 public class AddressViewModel
 {
   // (1)登録したエンティティ
   public AddressEntity Entity { get; set; }
   // (2)エラー発生時の例外
   public Exception Error { get; set; }
 }

このビューモデルでは、登録したエンティティ(1)と登録処理に失敗した場合のエラー情報を保持するための例外(2)の2つを定義します。

データを登録するアクション

続いてエンティティを登録するアクションをHomeコントローラに実装します。StorageAddressBook、AddressViewModelを使ってクライアントから渡されたエンティティの登録処理を行います。

HomeController.cs(抜粋)

 // (1)エンティティを扱うクラスのインスタンスを作成
 private StorageAddressBook addressBook = new StorageAddressBook();
 public ActionResult Add(AddressViewModel viewModel)
 {
   try
   {
     // (2)渡されたエンティティを登録する
     addressBook.Add(viewModel.Entity);
     return RedirectToAction("Index");
   }
   catch(Exception e)
   {
     // (3)例外が発生した場合にはビューモデルに渡してビューを表示
     viewModel.Error = e;
     return View(viewModel);
   }
 }

Homeコントローラではエンティティ操作用のStorageAddressBookをコントローラ作成時にフィールドに保持しておきます(1)。Addアクションでは後述するビューからビューモデルが渡されるため、Entityフィードに保持されているエンティティをStorageAddressBook#Addに渡してテーブルストレージへの登録を行います(2)。登録に成功した場合には一覧表示用のアクションにリダイレクトしています。もし登録時に例外が発生した場合にはcatch節によって捕まえたExceptionをビューモデルのErrorフィールドに設定し、ビューの描画を行います。

データ登録ビュー

次は登録フォームを表示するためのビューについて説明します。

Add.cshtml(抜粋)

 @* (1)コントローラと情報をやり取りするためのビューモデルを宣言 *@
 @model AzureTableStorageSample.Models.AddressViewModel
 ...中略...
 @if (Model != null && Model.Error != null)
 {
     // (2)例外が発生した場合にはメッセージを表示
     <span class="text-danger">@Model.Error.Message)</span>
 }
 @* (3)フォーム定義。HomeコントローラのAddアクションを呼び出す *@
 @using (Html.BeginForm("Add", "Home", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { role = "form" }))
 {
   ...中略...
   @* (4)入力するフィールドごとにTextBoxを用意する *@
   @Html.TextBoxFor(m => m.Entity.Email, new { @class = "form-control" })
   ...中略...
   @Html.TextBoxFor(m => m.Entity.LastName, new { @class = "form-control" })
   ...中略...
   @Html.TextBoxFor(m => m.Entity.FirstName, new { @class = "form-control" })
   ...中略...
   <input type="submit" value="登録" class="btn btn-primary" />
 }

 MVCの標準的なFormコンポーネントを利用して登録に必要な定義を行います。まず最初にこのビューで参照するモデルの宣言を行います(1)。エンティティとエラー情報をまとめて扱うためのビューモデルをモデルとして使用します。登録時にエラーが発生した場合には、どのような原因かをユーザに伝えるために、もしビューモデルのエラーがnullでない場合には、原因を表すメッセージを表示します(2)。  Html.BeginFormで、どのアクションに対して情報を表示するかの定義を行い(3)、その内部ではサーバに送信するためのプロパティを追加します(4)。

データの一覧を取得する

ここまででエンティティの登録が動作するようになりました。次は登録されたデータの表示について進めていきます。

ストレージからデータを取得する

テーブルストレージからデータを取得するには、TableQueryクラスを使用します。対象となるエンティティを絞り込みたい場合にはフィルタを作成し、TableQueryインスタンスに渡すことで種々の条件に対応したデータを取得することができます。

StorageAddressBook.cs(抜粋)

 public IEnumerable<AddressEntity> GetAll()
 {
   // (1)登録されているエンティティをすべて取得
   return table.ExecuteQuery(new TableQuery<AddressEntity>());
 }

本記事ではまず登録されているすべてのエンティティを取得する方法について見ていきます。といっても検索条件を指定しない場合の条件は単純で、取得したいエンティティの型を指定して作成したTableQueryインスタンスをCloudTable#ExecuteQuery()に渡すだけです(1)。

特定のパラメータなどを条件として絞り込みたい場合にはフィルタを指定することで実現することができます。次回ではこのあたりについても少し詳しく説明する予定です。

データを取得するアクション

すべてのエンティティを取得するアクションは、上記で説明したメソッドを利用してシンプルに次のように記述することができます。

HomeController.cs(抜粋)

 public ActionResult Index()
 {
   // (1)全データを取得
   var list = addressBook.GetAll();
   return View(list);
 }

StorageAddressBook#GetAll()メソッドで取得したすべてのエンティティコレクションをビューに渡して結果を表示させます。

データ一覧表示ビュー

最後は一覧表示を行うビューの定義です。

Index.cshtml(抜粋)

 @* (1)取得したエンティティのコレクションをモデルとして宣言 *@
 @model IEnumerable<AzureTableStorageSample.Models.AddressEntity>
 ...中略...
 <div>
 ...中略...
 @* (2)エンティティを一つずつ表示 *@
 @foreach (var address in Model)
 {
   <tr>
     <td>@address.Email</td>
     <td>@address.LastName</td>
     <td>@address.FirstName</td>
     <td>@address.Timestamp</td>
   </tr>
 }
 ...中略...
 </div>

一覧表示用のビューには、モデルとしてエンティティのコレクションが渡されるため、これをモデルとして宣言します(1)。このコレクションを@foreachにより一つずつ取り出してメールアドレス、姓、名を表示させるようにタグに埋め込んでいきます(2)。

開発環境で動作確認

 以上でプログラムの紹介は終わりです。まずは開発環境で実行してみましょう。

ストレージエミュレータへの接続設定

 開発環境では前回紹介したストレージエミュレータに値を保存することになります。前回は「App.config」ファイルに設定を記述しましたがASP.NET MVCではプロジェクト直下の「Web.config」」ファイルに記述するようになります。設定内容は前回と同じで次のように設定します。

Web.config(抜粋)

 <configuration>
   <appSettings>
     <add key="StorageConnectionString" value="UseDevelopmentStorage=true;" />
   </appSettings>
   ...中略...
 </configuration>

早速開発したプログラムを実行してみましょう。Windows 10の検索でAzure Storage Emulatorと検索し、起動します。VS 2015の[ソリューション構成]でDebugを指定し、Webアプリケーションを起動します。自動的にブラウザが開き、最初は何も登録されていない画面が開くと思います。ここで「追加」画面からメールアドレス、氏名を登録します。何件か登録し、一覧ページに移動してください。登録した値が表示されていることと思います。

前回インストールしたAzure Storage Emulatorを起動する

VS 2015の[ソリューション構成]でDebugを指定すると自動的に立ち上がる

ローカル環境のストレージに保存された内容を確認するには、VS2015のサーバーエクスプローラーを利用できます。サーバーエクスプローラーのツリーから[Azure]-[ストレージ]-[(開発)]と展開していくとBLOB、キュー、テーブルなどに登録されているデータの内容を確認することができます。

サーバーエクスプローラーでのストレージ内容確認

サンプルをApp Serviceに公開して動作確認

開発環境での動作確認が終わったらApp Serviceに公開して世界中からアクセスできるようにしましょう。App Serviceへの公開方法は第1回目の記事で紹介したやり方と同じですが、その前にAzureのストレージにアクセスするための設定を行う必要があります。

アクセスキーの設定

Azureストレージにデータを保存するには、アクセスキーが必要になります。アクセスキーおよびストレージアカウントについては前回の記事を御覧ください。 先程「Web.config」ファイルにエミュレータに接続する設定を記述しましたが、本番用の設定は「Web.Release.config」ファイルに記述します。

実際に設定すると次のようになります。key、value以外にも属性値が設定されていますが、これは一致したkey(Match(key))に対して、属性値(value)を「Web.config」に対して上書き設定(SetAttributes)するという操作を指示するためのものです。

Web.Release.config

 <add key="StorageConnectionString"
  value="DefaultEndpointsProtocol=https;AccountName=ストレージアカウント名;AccountKey=アクセスキー" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>

App Serviceへ公開

第1回目と同様に、「Webの1クリック発行」ツールバーボタンを使用し、App Serviceに公開します。開発環境と同じようにアドレスの登録、表示を試してみてください。またVS2015に搭載されているCloud Explorerを利用するとAzureストレージに登録されているデータを直接閲覧、操作することができます。

クラウドエクスプローラーによるストレージ内容確認

VS2015のCloud Explorerから、[サブスクリプション(図では従量課金)]-[Storage Accounts]-[作成したストレージアカウント]-[Tables]と階層を辿っていくと作成したテーブルストレージを確認することができます。停止するにはVS2015のサーバーエクスプローラーから発行プロファイルを選択し、右クリックメニューから[停止]を選択します。

まとめ

第3回目では、ストレージの中からテーブルストレージを取り上げて、具体的なコードを例にデータの登録と一覧取得を行う方法をASP.NET MVCを使用したWebアプリケーションと言うかたちで紹介しました。Webアプリケーションで使うには、他に更新や削除処理についても必要となる機会が多いと思います。次回はそれらCRUD処理をもう少し掘り下げて紹介する予定です。

WINGSプロジェクト 花田善仁著/山田祥寛監修
<WINGSプロジェクトについて>テクニカル執筆プロジェクト(代表山田祥寛)。海外記事の翻訳から、主にWeb開発分野の書籍・雑誌/Web記事の執筆、講演等を幅広く手がける。一緒に執筆をできる有志を募集中。