Kobarin's Development Blog

C#やASP.NETなどについての記録です。

ASP.NET MVCで独自DataAnnotation作成

MVCの検証に使うDataAnnotationはかなり便利で、標準のものだけでも例えば以下のようなものがあります。

  • Required : 必須項目化
  • Range : 値の範囲(例:1~100)
  • Compare : 他プロパティと同一判定(例:メルアドを2回入力した時、等)
  • RegularExpression : 正規表現(文字列の規則ならほぼ何でもあり)

これだけでも結構使えるんですが、WebFormsにあったような「greater than(より大きい)」や「less than(より小さい)」といった、他プロパティを参照するタイプの検証がCompareくらいしか用意されていません。
例えば「開始日」と「終了日」とか、「最安値」と「最高値」といった、あらかじめ上下関係が決まっている要素ってありますよね。
開始日が「2017/07/03」なのに「終了日」が「2016/06/30」ではおかしいので、こうした検証が必要な場面で使う属性がMVCにはありません。

DataAnnotation の自作?

DataAnnotationは完全独自で1からコーディングする事もできますし一番融通は効くと思われますが、やはりちょっと面倒です。興味ある方は以下の最上部の回答が参考になります。
stackoverflow.com

CustomValidation属性は?

RegularExpression等で対応しきれない場合、CustomValidationという手もあります。
エンティティ内に検証用コードを記述できるため自由度は高い反面、自作DataAnnotationのように再利用性は高くない上、やっぱりコーディングが面倒ですね。

ExpressiveAnnotations という選択肢

実際の開発の場ではほとんどは、それほど複雑な検証でなく先述のGreaterThanとかLessThan程度で済むレベルが多いのではないでしょうか。
探したところ、「ExpressiveAnnotations」という拡張機能があり、Nugetからインストールできます。

ExpressiveAnnotations の使い方

Nugetからインストールした後、エンティティ定義をします。
この例では、イベント情報をモデルに進めてみます。

using ExpressiveAnnotations.Attributes;


public class DemoEventModel
{
public int Id { get; set; }


[DisplayName("名称")]
[Required]
public string Name { get; set; }


[DisplayName("最安価格")]
public int? LowPrice { get; set; }


[DisplayName("最高価格")]
[AssertThat("LowPrice < HighPrice", ErrorMessage = "最安価格より金額が大きい必要があります")]
public int? HighPrice { get; set; }


[DisplayName("駐車場可")]
public bool ParkingAvailable { get; set; }


[DisplayName("駐車台数")]
[RequiredIf("ParkingAvailable == true", ErrorMessage = "駐車場可であれば、台数が必要です")]
public int? ParkingSpaces { get; set; }


[DisplayName("開始日")]
[AssertThat("OpenDate < CloseDate", ErrorMessage = "終了日より前である必要があります")]
public DateTime? SmallDate { get; set; }


[DisplayName("終了日")]
public DateTime? CloseDate { get; set; }
}

赤くしてある箇所が、ExpressiveAnnotations による追加部分です。AssertThatとRequireIfがあります。
AssertThatには、自プロパティも含めた式を記述します。複雑な数式も可能らしく、例えば「MaleRatio + FemaleRatio = 100」も可能なようです。
RequireIfは、特定条件下でのみ必須化したい場合に使います。上記では、「駐車場利用可能であれば、駐車可能台数を入力する」というものです。

クライアントサイドは?

上記ではサーバーサイドでの検証のみ有効となります。情報不足であるためクライアントサイドについても出来るか否か現在調べています。
分かり次第追記していきたいと思います。