情報応用演習Ⅰ(2024)

【T4b】モデルとビューの連携(後編)(3/6)

プロジェクトタイプ(注意: 本文参照)
プロジェクト名T4b
ソリューション名PIT4
注意
  • 本ページの作業内容は 前のページまでの続き になっていることに注意せよ.
    • 先に前のページまでをすべて読み,指示されている作業を済ませてから本ページを読むこと.
    • プロジェクトの作成作業については準備作業を参照せよ.

4b-3. 属性を使った検証などの指定

前節でみたように ASP.NET Core ではたとえば必須/任意入力といった制限はクラス定義から自動的に推定される. このほかにも,モデルクラスの各プロパティに属性を付加することによって,そのデータに関する 注釈や制限を細かく制御することが可能である.ちなみに属性というのはクラスや構造体といった型や, メソッド,メソッド引数,プロパティといったものの定義自体に何らかのメタデータを付加するC#の機能の一つである. 属性は今までの例で既に登場している.たとえば第1回のFooコントローラーのアクションメソッドに 付加した[HttpPost]が属性にあたる.データの注釈や制限に使用できる属性の一部を_にまとめる. 使用可能な属性についてのより詳しい説明については Model validation in ASP.NET Core MVC - Microsoft Docs を参照せよ.

データの注釈や制限に使用できる属性(一部)
↓属性\適用可能な対象→クラスフィールドプロパティメソッドメソッド引数意味
[Display]そのデータに対する汎用的な説明の文字列を指定する.
[Range]  その数値データの範囲(最小と最大)を指定する.
[StringLength]  その文字列データの長さを指定する.
[RegularExpression]  その文字列データが満たすべきパターン(正規表現)を指定する.
[Phone]  その文字列データが電話番号であることを指定する.
[EmailAddress]  その文字列データがeメールアドレスであることを指定する.
[CreditCard]  その文字列データがクレジットカード番号であることを指定する.
[EnumDataType] そのデータがC#の何らかの列挙型に対応することを指定する.
[DataType] そのデータのより詳しいデータ型を指定する.

本節ではこれらを使用してモデルクラスにより細かい制約を指定してみよう. これらの属性はSystem.ComponentModel.DataAnnotations名前空間で定義されているので1, 使用するにはそのソースコードの冒頭に_のような usingディレクティブを追記する必要がある.

System.ComponentModel.DataAnnotations名前空間の使用に必要なusingディレクティブ
1
using System.ComponentModel.DataAnnotations;

今回は前節で定義したHealthInfoクラスに注釈などを指定するので, Models/HealthInfo.cs の冒頭に_の usingディレクティブを追記しよう.まず各データの表示名を指定することにしよう. 現状のビューである Index.cshtml では体重(kg)とか身長(cm)といった データの表示名をビューに直接書き込んでいる.このようなデータは[Display]属性を使ってモデルクラス自体に関連付けることが可能である. モデルクラスに_のように追記してみよう.

「身長と体重」クラスへの追記内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class HealthInfo
{
    // 体重(kg)
    [Display(Name = "体重(kg)")]
    public double Weight { get; set; }

    // 身長(cm)
    [Display(Name = "身長(cm)")]
    public double Height { get; set; }

    // BMIの計算をする読み取り専用プロパティ
    public double BMI => Weight / Math.Pow(Height / 100, 2);
}

4行目,8行目では[Display]属性を 使用して各データの表示名を指定している2.これらを表示するにはビュー( .cshtml ファイル)にも指定が必要となる. Views/Home/Index.cshtml を_のように変更しよう.

Views/Home/Index.cshtmlへの追記内容1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@model HealthInfo

@{
    ViewData["Title"] = "Home Page";
    
    if (Model is null) throw new ArgumentNullException(nameof(Model)); // ad-hoc!: 非null保証のための回避策
}

<form asp-action="Index">
    @* 体重(Weight)のための入力欄 *@
    <label asp-for="@Model.Weight"></label>: <input asp-for="@Model.Weight" />
    <span asp-validation-for="@Model.Weight"></span>
    <br />
    
    @* 身長(Height)のための入力欄 *@
    <label asp-for="@Model.Height"></label>: <input asp-for="@Model.Height" />
    <span asp-validation-for="@Model.Height"></span>
    <br />
    
    <input type="submit" value="送信" />
</form>

@section Scripts
{
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

体重(kg)身長(cm)といった表示名を直接書く代わりに, label 要素を使用している. label 要素自体はフォームの入力欄の見出しに使用されるHTMLの要素であるが, asp-for タグヘルパーを使用することによって[Display]属性で指定した値が表示されるようになる.

ここまで書き換えたらスタートアッププロジェクトをT4bに切り替えて実行してみよう. 実行結果は_のようになるはずである.

実行結果

_のように表示されていれば正しく操作できているが,これでは先ほどと表示が同じなので[Display]属性や asp-for タグヘルパーをともなった label 要素の効果が分かりにくい.そこで,いったん終了して試しにWeightおよびHeightプロパティの表示名の設定を _に示す通りに変更してみよう.

「身長と体重」クラスの変更内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class HealthInfo
{
    // 体重(kg)
    [Display(Name = "ほげ")]
    public double Weight { get; set; }

    // 身長(cm)
    [Display(Name = "ぴよ")]
    public double Height { get; set; }

    // BMIの計算をする読み取り専用プロパティ
    public double BMI => Weight / Math.Pow(Height / 100, 2);
}

ここまで書き換えたら実行してみよう.実行結果は_に示す通り,入力欄のまえの見出しが _で書き換えた通りに反映されているはずである.

実行結果

ここまでを確認したら,__に示す通りHealthInfoクラスの定義を_の 定義に戻して実行し,変更が反映されることを確認しておこう.

読者らの中には「 .cshtml ファイルに『体重(kg)』や『身長(cm)』といったラベルを直接書いたほうが簡単なのでは? 」と 思うものもいるかもしれない.しかし,ここでの例のように各入力項目の定義や注釈といった情報を, 一か所(ここではクラス定義)に集約できること は大変な利点がある.たとえば,Weightプロパティや Heightプロパティはこのページ以外でも使用するかもしれない.そのような状況で,各プロパティの 表示名を後から変更したくなったとしたらどうだろうか. .cshtml ファイルに直接ラベルを書き込む方法では, WeightプロパティやHeightプロパティを参照している .cshtml ファイルをすべてチェックして, 古いラベルを書き換えなければならなくなってしまう.これはいかにも間違いを発生させやすい状況である. 上記のように[Display]属性と asp-for タグヘルパーを用いる方法であれば,このような煩雑な更新作業は 不要となる.その場合はクラス定義の[Display]属性を書き換えれば済むためである.

これは ASP.NET Core に限った話ではないが,プログラミングにおいては DRY原則 は 非常に重要であることを覚えておこう.DRYとは "Don't Repeat Yourself" の頭字語であり,同じ作業を手動で繰り返さなければ一貫性を保てないような設計は避けるべきである ,という 考え方である.今回学んだデータアノテーションのように,データの定義は一度だけ一か所のみで行う ことには様々な利点があることを理解しておこう.


  1. 属性はこの名前空間以外で定義されているものもある. ↩︎

  2. なおBMIプロパティはここでは表示に直接使用しない読み取り専用のプロパティであるため属性の設定を省略している. ↩︎

Last updated on 2024-04-24
Published on 2024-04-24

Powered by Hugo. Theme by TechDoc. Designed by Thingsym.