ダウンロード サンプルファイル (185.0 KB)
複雑な画面のモデル検証
最後に、少し複雑な画面でモデル検証を行う方法を簡単に紹介しましょう。例には予約情報編集画面を用います。ここで、「複雑」とは、①DBの項目と画面の項目が1対1ではない、②検証属性で処理できない独自のチェックが必要、といったことを表します。
DBの項目と画面の項目が1対1ではない場合の対応
まず、DBの項目と画面の項目が1対1ではない場合についてです。このケースへはDBと画面は別物として考え、モデルバインド、検証用にViewModel(ビューモデル)を用意します。これは、主にMVC系のパターンを用いたPDS(Presentaion Domain Separation、プレゼンテーションとドメインの分離(注1))の実現方法と同じ考え方です。
例では、予約情報編集画面のために、App_Codeフォルダに新たにReservationViewModel.csというクラスを用意しています(リスト6)。このクラスには、画面の項目と1対1になるようなプロパティを定義し、それぞれに検証属性をつけています。具体的には、予約開始/終了日時が画面では日付と時刻に分かれていますが、DBでは1つの項目です。そこで、日付、時刻それぞれのプロパティを用意しています。
コードをユーザーインターフェース(プレゼンテーション)とその他の機能(ドメイン)の責務で分割することです。プレゼンテーション・プラットフォームの都合が関係ある処理とそうでない処理を分割することで、理解のしやすさを向上させたり、メンテナンスを容易にすることが可能です。
詳しくは、「Martin Fowler's Bliki in Japanese-プレゼンテーションとドメインの分離」を参照してください。
public class ReservationsDetailViewModel
{
public int ReservationId { get; set; }
public int LocationId { get; set; }
[Required(ErrorMessage = "会議室を選択してください。")]
public string MeetingRoomId { get; set; }
[Required(ErrorMessage = "予約開始日を入力してください。")]
public string DateOfReserveDateFrom { get; set; }
[Required(ErrorMessage = "予約開始時刻を入力してください。")]
public string TimeOfReserveDateFrom { get; set; }
[Required(ErrorMessage = "予約開始日を入力してください。")]
public string DateOfReserveDateTo { get; set; }
[Required(ErrorMessage = "予約開始時刻を入力してください。")]
public string TimeOfReserveDateTo { get; set; }
public string Purpose { get; set; }
public string Remarks { get; set; }
public byte[] Version { get; set; }
…(中略)…
}
ただし、このやり方はビジネスロジックを呼び出す際、ViewModelとDBから取り出したオブジェクトの間で、値の詰め替えがどうしても発生します(リスト7)。項目数が多い場合は、「AutoMapper」などのサポートライブラリの使用も検討しましょう。
public ReservationsDetailViewModel SelectReservation([QueryString]int reservationId)
{
Reservation reservation;
// 削除後に検索処理が呼ばれるため、同じデータを返すようSessionに入れておく
if (Session["reservation"] != null)
{
reservation = Session["reservation"] as Reservation;
}
else
{
var logic = new ReservationLogic();
reservation = logic.Find(reservationId);
Session["reservation"] = reservation;
}
return new ReservationsDetailViewModel
{
ReservationId = reservation.ReservationId,
LocationId = reservation.MeetingRoom.LocationId,
MeetingRoomId = reservation.MeetingRoomId.ToString(),
DateOfReserveDateFrom = reservation.ReserveDateFrom.ToString("yyyy/MM/dd"),
TimeOfReserveDateFrom = reservation.ReserveDateFrom.ToString("HH:mm"),
DateOfReserveDateTo = reservation.ReserveDateTo.ToString("yyyy/MM/dd"),
TimeOfReserveDateTo = reservation.ReserveDateTo.ToString("HH:mm"),
Purpose = reservation.Purpose,
Remarks = reservation.Remarks,
Version = reservation.Version,
};
}
独自のチェック実行方法
次に、独自のチェックを行う方法を説明します。これは以下の方法で実現ができます。
- バインド先モデルにIValidatableObjectインターフェースを実装する
- IValidatableObject.Validateを定義する
予約情報編集画面では、予約開始日/時刻、予約終了日/時刻の形式チェック並びに前後関係チェックをValidateメソッドで行っています(リスト8)。
public class ReservationsDetailViewModel : IValidatableObject
{
…(中略)…
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!String.IsNullOrEmpty(DateOfReserveDateFrom))
{
if (!DateTimeUtilities.IsDate(DateOfReserveDateFrom))
{
yield return new ValidationResult("予約開始日には日付(yyyy/MM/dd形式)を入力してください。");
}
}
if (!String.IsNullOrEmpty(TimeOfReserveDateFrom))
{
if (!DateTimeUtilities.IsTime(TimeOfReserveDateFrom))
{
yield return new ValidationResult("予約開始時刻には時刻(HH:mm形式)を入力してください。");
}
}
…(中略)…
}
}
なお、Validateメソッドでは戻り値のValidationResult型オブジェクトをyield returnで返すことで、ValidationSummaryコントロールにそのメッセージを表示することができます。ValidationResult型には、コンストラクタ引数にエラーメッセージを指定します。
以上で、複雑なモデル検証の実装は終わりです。紙面の都合で実行結果は掲載しませんが、各自でサンプルを動かして動作を確認してみてください。
また、このままの状態ではクライアント検証は動作しません。動作させるにはどのように修正すればよいか、挑戦してみるのもよいでしょう。
まとめ
今回はモデルバインドを使う際には欠かせない、モデル検証の概念、実装方法について学びました。ポイントをまとめると、次のようになります。
-
モデルバインドは検証処理をViewからModel側に集約する
- 検証ロジックをUIから分離できる
- 複数画面で同じ検証ロジックを使いまわせる
-
モデル検証を行うには、次の手順が必要
- モデルのプロパティにSystem.ComponentModel.DataAnnotations名前空間の検証属性をつける
- データバインドコントロールでモデル検証を有効にする
- Page.ModelStateプロパティのIsValidプロパティで検証結果を判定する
-
単にモデル検証を使うだけではクライアントサイド検証は行われない
- クライアントサイド検証を行うには、Dynamic DataのDynamicコントロールやDynamicFieldを活用する
-
複雑な画面でモデル検証を行うには、少し手間が必要
-
画面とDBの項目が1対1でない
- 画面専用のViewModelを用意し、PDS(Presentation Domain Separation)を図る
-
独自の検証処理が必要
- IValidatableObjectインターフェースを実装し、Validateメソッドを定義する
-
画面とDBの項目が1対1でない
今回までの流れで、主要なASP.NET 4.5の新機能は紹介できたのではないかと考えています。次回は残りの新機能について、ざっと紹介する予定です。お楽しみに。
この記事は参考になりましたか?
既に会員の方はを行ってください。
- 実例で学ぶASP.NET 4.5 Webフォーム 新機能活用法連載記事一覧
- この記事の著者
-
山田 祥寛(ヤマダ ヨシヒロ)
静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
-
WINGSプロジェクト 高野 将(タカノ ショウ)
<個人紹介>新潟県長岡市在住の在宅リモートワークプログラマー。家事や育児、仕事の合間に長岡IT開発者勉強会(NDS)、Niigata.NET、TDDBCなどのコミュニティに関わったり、Web記事や書籍などの執筆を行ったりしている。著書に『アプリを作ろう! Visual C#入門 Visual C# 2017対応』(日経BP社、2017)など。< WINGSプロジェクト について>有限会社 WINGSプロジェクト が運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での 登録メンバ は約50名で、現在も執筆メンバを 募集中 。興味のある方は、どしどし応募頂きたい。 著書 、 記事 多数。 RSS X: @WingsPro_info (公式)、 @WingsPro_info/wings (メンバーリスト) Facebook
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
この記事は参考になりましたか?
この記事をシェア
