はじめに
前々回より始まったストレージについての紹介も、今回で最後となります。本記事ではテーブルストレージを用いたWebアプリケーションを作る上で必要となる、更新、削除について紹介します。
ところで、先日発表されたVisual Studio 2017はもうお試しでしょうか?今回からは次のように、新しいVisual Studioを用いてサンプルの作成や動作確認を行っていきます。
・Windows 10 Home
・Visual Studio Community 2017(以降、VS2017)
本稿で説明するリストはサンプルの中に収録しています。ご活用ください。
Azureと統合されたVS2017
VS2015までは、Azureなど統合開発環境に直接組み込まれていない機能については、別途複数のSDKを入手してインストールする必要がありました。VS2017では何をやりたいのかという目的に応じて、必要な機能をまとめて管理するための単位としてワークロードという概念が導入され、開発者のやりたい環境を簡単に構築することができるようになりました。
VS2017のインストール
VS2017をインストールするには、Visual Studioのサイトからインストーラをダウンロードして実行します。インストーラを実行していくと次のようなワークロードの選択画面が表示されますので、本連載で必要となる「ASP.NETとWeb開発」、「Azureの開発」、「データの保存と処理」の3つを選択します。
単一のエンティティを取得する
本記事では前回作成したWebアプリケーションに、更新、削除を行う機能を追加します。特に更新と削除では操作対象となるエンティティを一意に特定する必要がありますが、そのために用意されているテーブルオペレーション(TableOperation)である、Retrieveを最初に紹介します。
StorageAddressBook.cs(抜粋)
public AddressEntity GetByKey(string partitionKey, string rowKey)
{
// (1)単一エンティティ取得用オペレーション作成
TableOperation retrieveOperation = TableOperation.Retrieve<AddressEntity>(partitionKey, rowKey);
// (2)検索を実行し結果を返す
TableResult retrievedResult = table.Execute(retrieveOperation);
return retrievedResult.Result as AddressEntity;
}
テーブルストレージでは、エンティティを一意に識別するためのキーとしてPartitionKeyとRowKeyが存在します。この2つのキーを指定してRetrieveオペレーションを作成します(1)。作成されたRetrieveオペレーションをCloudTable#Executeメソッドに渡すことで、検索を実行します。検索結果はTableResult.Resultに格納されており、見つかればエンティティオブジェクトが、見つからなければnullが割り当てられています。
エンティティを更新する
エンティティを取得するメソッドができましたので、まずは更新処理を行うサンプルを紹介します。まずは一覧表示用のビューテンプレートファイル(Index.cshtml)に、次のようにエンティティの2つのキーをパラメータとして編集画面に遷移するリンクを記述した列を追加します。
Index.cshtml(抜粋)
...中略...
<td>
@Html.ActionLink("編集", "Update", new { partitionKey = address.PartitionKey, rowKey = address.RowKey })
</td>
...中略...
エンティティの編集画面
上記リンクから遷移する先は、HTTPのGETメソッドに対応するUpdateアクションとなります。コントローラ側の実装は次のようになります。
HomeController.cs(抜粋)
[HttpGet]
public ActionResult Update(string partitionKey, string rowKey)
{
// (1)2つのキーを指定して一致するエンティティを取得
AddressEntity entity = addressBook.GetByKey(partitionKey, rowKey);
// (2)見つからなかった場合にはIndexにリダイレクト
if (entity == null)
{
return RedirectToAction("Index");
}
// (3)ViewModelを作成し、更新対象のエンティティを設定しビューを表示
AddressViewModel viewModel = new AddressViewModel();
viewModel.Entity = entity;
return View(viewModel);
}
パラメータとして受け取った2つのキーを先程作成したメソッドに渡してエンティティを取得します(1)。ここでもしエンティティが見つからなかった場合には一覧画面にリダイレクトすることにします(2)。そして無事エンティティを取得できたらViewModelに設定し、編集用のビューテンプレート(Update.cshtml)に渡して編集画面をブラウザに返します(3)。
編集用のビューは、前回説明した追加用のテンプレートとほぼ同じのため、ここでは省略させていただきます。
エンティティを更新する
編集画面からは変更したエンティティデータがViewModelに設定されて、HTTPのPOSTメソッドでUpdateアクションに送信されます。
HomeController.cs(抜粋)
[HttpPost]
public ActionResult Update(AddressViewModel viewModel)
{
try
{
// (1)Address編集フォームより送信されたエンティティを更新
AddressEntity entity = addressBook.Update(viewModel.Entity);
if (entity != null)
{
// (2)正常に更新
viewModel.Entity = entity;
}
else
{
// (3)該当するエンティティが見つからなかった
throw new ArgumentException("見つかりませんでした:" + viewModel.Entity.Email);
}
}
catch (Exception e)
{
// (4)例外が発生
viewModel.Error = e;
}
return View(viewModel);
}
ViewModelに設定されたエンティティを、後述する更新ロジックメソッドに渡してテーブルストレージへ反映させます(1)。正常に更新できた場合にはメソッドの戻り値としてエンティティを返却するように実装しているので、返ってきたエンティティをViewModelに設定します(2)。もしnullが返ってきた場合には、それは該当するエンティティが見つからなかったという意味なので、例外を投げるようにしています(3)。対象となるエンティティが見つからない場合など、発生した例外は(4)の部分でまとめてキャッチし、ViewModelのErrorプロパティに設定します。 次に、更新されたエンティティデータをテーブルストレージに反映するロジックを実装したメソッドを見ていきましょう。登録を行うためにはテーブルオペレーションとしてInsertを使用しましたが、更新の際にはReplaceを使用します。
Webアプリケーションに限らず、クラウド上に実装されたアプリケーションは様々な場所からアクセスされることが予想されるため、テーブルストレージではエンティティが取得された後にサーバ側で何らかの変更があった場合には、更新処理は失敗するようになっています。これを防ぐため、クライアントから渡されたエンティティをそのままReplaceオペレーションに渡すのではなく、一旦最新のエンティティを取得し、そのプロパティを更新する形で処理する必要があります。そのことを踏まえて更新ロジックを見ていきましょう。
StorageAddressBook.cs(抜粋)
public AddressEntity Update(AddressEntity entity)
{
// (1)渡されたエンティティのキーから検索
AddressEntity updateEntity = GetByKey(entity.PartitionKey, entity.RowKey);
if (updateEntity != null)
{
// (2)見つかったエンティティに更新用の値を設定
updateEntity.LastName = entity.LastName;
updateEntity.FirstName = entity.FirstName;
// (3)入れ替えオペレーションを作成し、実行
TableOperation replaceOperation = TableOperation.Replace(updateEntity);
TableResult result = table.Execute(replaceOperation);
// (4)入れ替え後のエンティティを返す
return result.Result as AddressEntity;
}
// (5)見つからなかった場合にはnullを返す
return null;
}
まず最初に更新対象となるエンティティの2つのキーを使用して、最新のエンティティをテーブルストレージから取得します(1)。無事対象エンティティが見つかった場合には、更新するプロパティを見つかったエンティティにコピーします(2)。値をコピーされた最新のエンティティ用いてReplaceオペレーションを作成して実行します(3)。無事に更新処理が成功したらエンティティが返ってくるため、メソッドの戻り値として返します(4)。他のユーザが削除したなどの理由で対象エンティティが見つからなかったなどの場合にはnullを返します(5)。
エンティティを削除する
次に削除処理を行うサンプルを見ていきましょう。更新の場合と同様に一覧画面に削除用のフォームを定義した列を追加します。一意のエンティティを識別するために2つのキーをHiddenパラメータとして渡しています。
Index.cshtml(抜粋)
...中略...
<td>
@using (Html.BeginForm("Delete", "Home", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { role = "form" }))
{
@Html.AntiForgeryToken()
@Html.Hidden("partitionKey", address.PartitionKey)
@Html.Hidden("rowKey", address.RowKey)
<input type="submit" value="削除" />
}
</td>
...中略...
上記のフォームからはHTTPのPOSTメソッドでDeleteアクションが実行されます。コントローラの実装は次のようになります。
HomeController.cs(抜粋)
[HttpPost]
public ActionResult Delete(string partitionKey, string rowKey)
{
// (1)指定したメールアドレスに該当するエンティティを削除
addressBook.Delete(partitionKey, rowKey);
// (2)Indexにリダイレクト
return RedirectToAction("Index");
}
フォームから渡された2つのキーを、後述する削除ロジックに渡して処理を実行します(1)。このサンプルでは削除したエンティティを使って何かを行うことはないため、そのまま一覧画面にリダイレクトします(2)。
削除の場合にも更新と同様に、意図しない削除を防ぐために最新のエンティティをDeleteオペレーションに渡すことで実行する必要があります。削除ロジックは次のようになります。
StorageAddressBook.cs(抜粋)
public AddressEntity Delete(string partitionKey, string rowKey)
{
// (1)渡されたエンティティをキーから検索
AddressEntity deleteEntity = GetByKey(partitionKey, rowKey);
if (deleteEntity != null)
{
// (2)ストレージに存在する場合には削除オペレーションを作成し、実行
TableOperation deleteOperation = TableOperation.Delete(deleteEntity);
TableResult result = table.Execute(deleteOperation);
// (3)削除されたエンティティを返す
return result.Result as AddressEntity;
}
// (4)見つからなかった場合にはnullを返す
return null;
}
更新の場合と同様に、2つのキーを使用して最新のエンティティを取得します(1)。ここでテーブルストレージから該当するエンティティが見つかった場合には、そのエンティティを用いてDeleteオペレーションを作成して実行します(2)。削除の場合にも該当エンティティが返ってくるため、コントローラ側で何らかの処理を行う場合に備えて、メソッドの戻り値として返します(3)。すでに他のユーザに削除されているなどの理由で、対象となるエンティティが見つからない場合にはnullを返します(4)。
その他のテーブルオペレーション
これまで紹介したテーブルオペレーションは、登録(Insert)、更新(Replace)、削除(Delete)、取得(Retrieve)の4種類ですが、これ以外にも用途によって使い分けられるように幾つかのオペレーションが用意されています。
その他のテーブルオペレーション
名称 | 説明 |
---|---|
Merge | Updateは渡されたエンティティで置き換えるのに対して、Mergeは渡されたエンティティを既存のエンティティにマージする |
InsertOrReplace | キーの一致するエンティティが見つからない場合には登録、存在する場合には置換 |
InsertOrMerge | キーの一致するエンティティが見つからない場合には登録、存在する場合にはマージ |
サンプルを動作確認
それではサンプルを実行してみましょう。ローカルのPCで実行する場合にはStorage Emulatorを起動することを忘れないでください。また、Azureにストレージアカウントを持っている方はApp Serviceに公開して確認してみてください。なお、それぞれの具体的な手順などは前回までの記事をご参照ください。
[追加]画面からデータを追加できる |
メールアドレス、名前を入力して[登録]ボタンを押すと登録されていく |
一覧画面では各データに[編集]と[削除]が表示され、データの編集や削除が行えるようになっている |
検索画面からは登録各データを検索できる |
なお、サンプルにはドメイン名などを組み合わせた検索を行う機能についても実装しています。ご興味のある方は御覧ください。またサンプルを実行する際には「NuGetパッケージの復元」を行うこと、App Serviceに公開後に不要になったリソースは停止させることをお忘れなく。
Storage Explorerを利用してストレージの中身を確認
前回ではAzure上のストレージデータを確認するにはVS2015(2017も同様)のクラウドエクスプローラを使用する方法を紹介していましたが、これ以外にもAzure上のストレージを確認する方法があります。
Azure上のストレージ内容を確認する他の方法として、Azure Storage Explorer(以降、Storage Explorer)を利用することができます。これはWindowsだけでなくMacやLinuxでも実行することのできるアプリケーションで、Azure上のデータだけではなくローカルのデータへもアクセスすることできます。
このツールをインストールするにはStorage ExplorerのサイトMicrosoft Azure Storage Explorerにアクセスし、使用しているOS用のインストーラをダウンロードします。ダウンロードしたファイル「StorageExplorer.exe」をダブルクリックし、インストーラを起動してください。
起動してAzureのアカウントでログインすると、次のようにローカルだけでなくAzureのストレージに保存された内容を閲覧することができます。
[コラム]従量課金サブスクリプションの登録
連載からしばらく立っているため、30日間無料試用版サブスクリプションの有効期限が切れている方もいらっしゃると思います。ここでは従量課金サブスクリプションに登録するやり方を紹介します。なお、この内容は2017年1月時点でのものであり、将来変更される場合があることをご承知おきください。 Azureにサインし、ダッシュボードの左側にあるリソース一覧から[課金]を選択して課金ビューを開きます。そして中央上部にある[新しいサブスクリプション]をクリックしてください。 別のページが開き、プラン選択画面が表示されます。開発者プログラム特典など複数の選択肢が提示されていますので、ご都合にあったプランを選んでください。今回は従量課金を選択します。 新規に作成するか、trial(無料試用版)からアップグレードするかを選択できますので、無料試用版で幾つかのリソースを作成しており、そのまま継続して利用したい場合にはtrialからのアップグレードを選択してください。 次の画面でアップグレードを完了しますが、もし無料試用版の期間中の場合には特典を引き継ぐことができます。
まとめ
3回にわたって紹介してきましたテーブルストレージについても今回で終りとなります。データを保存するという用途としてはキューやファイルストレージなどの便利な活用方法を思いついていただけたでしょうか?次回からはAzure上のSQL Databaseについて紹介する予定です。
WINGSプロジェクト 花田善仁著/山田祥寛監修
<WINGSプロジェクトについて>テクニカル執筆プロジェクト(代表山田祥寛)。海外記事の翻訳から、主にWeb開発分野の書籍・雑誌/Web記事の執筆、講演等を幅広く手がける。一緒に執筆をできる有志を募集中。