前回の記事で簡単なメッセージ表示を行うものを作成したものの続きです。今回は、メッセージ更新の際にメッセージ作成者が決めたキーワードを確認するように変更を行い、構築済みの Windows Azure のテーブル ストレージに対してデータ項目の追加を行ったときの振る舞いを確認してみます。
確認した結果は次のとおりです。
- データ項目の追加は問題なく行える。
- データ項目を追加する前に作成されていたレコードにアクセスし、追加した項目を読み取ると「null」が得られる。
- データ項目を追加する前に作成されていたレコードにアクセスし、追加した項目に対してデータを設定することも問題なくできる。
確認したときのスクリーンショットは次のとおり。
※3点目は IMessage インターフェイスに「備考」欄を追加することで確認しました(冗長になるのでコードとスクリーンショットは載せていません)。
ちなみに、画面はこんな感じになっています。
プログラムの変更内容は以下のとおりです。
まずはモデルの変更になりますが、プロパティをインターフェイスで規定しているので、「Models」フォルダの「IMessage.cs」から変更していきます。プロパティに「UpdateKey」を追加します。
using System.ComponentModel.DataAnnotations;
namespace MvcWebRole1.Models
{
public interface IMessage
{
[Display(Name = "メッセージ")]
string MessageBody { get; set; }
[Display(Name = "ユーザー名")]
string UserName { get; set; }
[Display(Name = "修正用のキーワード")]
string UpdateKey { get; set; }
string PartitionKey { get; set; }
string RowKey { get; set; }
}
}
次に同フォルダの「Message.cs」を変更します。プロパティに「UpdateKey」を追加します。
namespace MvcWebRole1.Models
{
public class Message : IMessage
{
public string MessageBody { get; set; }
public string UserName { get; set; }
public string UpdateKey { get; set; }
public string PartitionKey { get; set; }
public string RowKey { get; set; }
}
}
次にデータアクセス部分を変更します。
まずは テーブル ストレージのエンティティから。「DAL」フォルダの「MessageEntity.cs」を変更します。プロパティに「UpdateKey」を追加します。
using System;
using Microsoft.WindowsAzure.StorageClient;
using MvcWebRole1.Models;
namespace MvcWebRole1.DAL
{
public class MessageEntity : TableServiceEntity, IMessage
{
public string MessageBody { get; set; }
public string UserName { get; set; }
public string UpdateKey { get; set; }
public MessageEntity()
{
this.PartitionKey = DateTime.UtcNow.ToString("yyyyMMdd");
this.RowKey = Guid.NewGuid().ToString();
}
}
}
次にリポジトリですが、IMessageRepository インターフェイスに変更はない(IMessage インターフェイスが変更点を内包している)ので、「DAL」フォルダの「MessageRepository.cs」を変更します。Add メソッドでキーワードの格納、Update メソッドと Remove メソッドでキーワードの確認(一致しない場合には前回定義しておいた UpdateException を投げます)、entity2Item メソッドでモデルのキーワードへ string.Empty のセットを行います。
//
public void Add(IMessage message)
{
var entity = new MessageEntity();
entity.MessageBody = message.MessageBody;
entity.UserName = message.UserName;
entity.UpdateKey = message.UpdateKey;
_context.AddObject(EntitySetName, entity);
_context.SaveChanges();
}
public void Update(IMessage message)
{
var dbEntity = getEntity(message.PartitionKey, message.RowKey);
if (message.UpdateKey != dbEntity.UpdateKey)
{
throw new UpdateException("更新用のキーワードが一致しません。");
}
dbEntity.MessageBody = message.MessageBody;
dbEntity.UserName = message.UserName;
_context.UpdateObject(dbEntity);
_context.SaveChanges();
}
public void Remove(IMessage message)
{
var dbEntity = getEntity(message.PartitionKey, message.RowKey);
if (message.UpdateKey != dbEntity.UpdateKey)
{
throw new UpdateException("更新用のキーワードが一致しません。");
}
_context.DeleteObject(dbEntity);
_context.SaveChanges();
}
private static Message entity2Item(MessageEntity entity)
{
var item = new Message();
item.MessageBody = entity.MessageBody;
item.UserName = entity.UserName;
item.UpdateKey = string.Empty;
item.PartitionKey = entity.PartitionKey;
item.RowKey = entity.RowKey;
return item;
}
次にコントローラーです。メッセージ修正用のキーワードが一致しないときに、「UpdateException」が発生するので、この例外をハンドリングします。対象となるアクションは Edit, Delete です。
//
[HttpPost]
public ActionResult Edit(Message message)
{
if (ModelState.IsValid)
{
try
{
_repository.Update(message);
return RedirectToAction("Index");
}
catch (UpdateException ex)
{
ModelState.AddModelError("UpdateKey", ex.Message);
}
}
return View(message);
}
[HttpPost]
public ActionResult Delete(Message message)
{
if (ModelState.IsValid)
{
try
{
_repository.Update(message);
return RedirectToAction("Index");
}
catch (UpdateException ex)
{
ModelState.AddModelError("UpdateKey", ex.Message);
}
}
return View(message);
}
最後にビューです。メッセージ修正用のキーワードについての変更なので、Create, Edit, Delete のビューが修正対象になります。
Create.cshtml
@model MvcWebRole1.Models.IMessage
@{
ViewBag.Title = "Create";
}
<h2>Create</h2>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>MessageEntity</legend>
<div class="editor-label">
@Html.LabelFor(model => model.MessageBody)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.MessageBody)
@Html.ValidationMessageFor(model => model.MessageBody)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.UserName)
@Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.UpdateKey)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.UpdateKey)
@Html.ValidationMessageFor(model => model.UpdateKey)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
Edit.cshtml
@model MvcWebRole1.Models.IMessage
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>IMessage</legend>
<div class="editor-label">
@Html.LabelFor(model => model.MessageBody)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.MessageBody)
@Html.ValidationMessageFor(model => model.MessageBody)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.UserName)
@Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.UpdateKey)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.UpdateKey)
@Html.ValidationMessageFor(model => model.UpdateKey)
</div>
@Html.HiddenFor(model => model.PartitionKey)
@Html.HiddenFor(model => model.RowKey)
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
Delete.cshtml
@model MvcWebRole1.Models.IMessage
@{
ViewBag.Title = "Delete";
}
<h2>Delete</h2>
<h3>このデータを削除しますか?</h3>
<fieldset>
<legend>メッセージ</legend>
<div class="display-label">
@Html.DisplayNameFor(model => model.MessageBody)
</div>
<div class="display-field">
@Html.DisplayFor(model => model.MessageBody)
</div>
<div class="display-label">
@Html.DisplayNameFor(model => model.UserName)
</div>
<div class="display-field">
@Html.DisplayFor(model => model.UserName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.UpdateKey)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.UpdateKey)
@Html.ValidationMessageFor(model => model.UpdateKey)
</div>
@Html.HiddenFor(model => model.PartitionKey)
@Html.HiddenFor(model => model.RowKey)
</fieldset>
@using (Html.BeginForm()) {
<p>
<input type="submit" value="Delete" /> |
@Html.ActionLink("Back to List", "Index")
</p>
}
ここまでで「メッセージ更新の際にメッセージ作成者が決めたキーワードを確認する」機能追加が完了です。
このプログラムは、ポイントのみを抜き出していることから、次のような事柄への対応を行なっていません。
- 一覧画面でのメッセージの列挙順が定まっていない
- 要求されたキーを持つメッセージがなかったときに例外が発生する
1点目については、Windows Azure テーブルストレージへのクエリでは OrderBy が使用できないため、対応には一工夫が必要です。
2点目については、例外への対応方法が たんたか さんの記事に詳しく書いてあります。
「Azure のテーブル ストレージ を ASP.NET MVC で使ってみる(その2)」への1件のフィードバック