WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

ASP.NET MVC 用の CompareValidator

by WebSurfer 2018年12月31日 12:06

ASP.NET Web Forms には CompareValidator という検証コントロールがあって、2 つの TextBox に入力された値の大小比較をして検証できる機能があります。

それと同様な検証機能を ASP.NET MVC で実装する方法を書きます。(元の話は Teratail のスレッド ASP.NET MVC4 TimeSpan型入力値の検証です)

MVC の CompareValidator

ASP.NET MVC には CompareAttribute という検証用のデータアノテーション属性がありますが、それには大小を比較する機能はなく、同じか否かを検証するのみです。

なので、例えば、ユーザーが開始時間と終了時間をテキストボックスに入力した際に、終了時間が開始時間より後になっていることを検証するような場合は、カスタム検証機能を実装することになります。

複数のプロパティにまたがった検証をする場合、Teratail のスレッド「MVC モデルのバリデーションについて」の回答に書いたように、CustomValidationAttribute を使うか、モデルに IValidatableObject インターフェイスを実装するという手段を使うのが普通のようです。

しかし、2 つのテキストボックス入力の大小比較程度なら、CustomValidationAttribute を使わなくても、リフレクションを使って比較対象のプロパティの値を取得し、それと自身のプロパティの値を比較して検証結果を返すということができます。

参考にしたのは Compare Validator in MVC という記事の Limitations of the Compare DataAnnotation 以下のセクションです。

上の画像を表示するのに使ったサンプルコードは以下の通りです。TimeSpan 型のプロパティを検証の対象としていますが、DataTime 型その他でもキャストを変更して同様に検証可能です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Globalization;

namespace Mvc5App.Models
{
  public class KintaiModel
  {
    public IList<Record> Records { get; set; }
  }

  public class Record
  {
    public int Id { set; get; }
    public string Week { set; get; }
    public TimeSpan Open { set; get; }

    [TimeGreaterThan("Open", 
                   "{0} は Open より後に設定してください")]
    public TimeSpan Close { set; get; }

    // ・・・中略・・・
  }

  [AttributeUsage(AttributeTargets.Property)]
  public class TimeGreaterThanAttribute : ValidationAttribute
  {
    private string _startTimePropertyName;

    // コンストラクタ
    public TimeGreaterThanAttribute(
            string startTimePropertyName, string errorMsg)
    {
      this._startTimePropertyName = startTimePropertyName;
      this.ErrorMessage = errorMsg;
    }

    public override string FormatErrorMessage(string name)
    {
      return string.Format(CultureInfo.CurrentCulture,
                           ErrorMessageString, name);
    }

    protected override ValidationResult IsValid (
        object value, ValidationContext validationContent)
    {
      // ObjectType はこの属性を付与したプロパティが属する
      // クラス、即ち上のコードの Record クラスとなる
      System.Reflection.PropertyInfo propertyInfo = 
          validationContent.ObjectType.
          GetProperty(this._startTimePropertyName);
      object propertyValue = 
          propertyInfo.
          GetValue(validationContent.ObjectInstance, null);

      if ((TimeSpan)value > (TimeSpan)propertyValue)
      {
        return ValidationResult.Success;
      }
      else
      {
        // ValidationResult の引数に null または "" を設定する
        // と FormatErrorMessage メソッドの戻り値が設定される
        return new ValidationResult(null);
      }
    }
  }
}

上の画像・コードではクラスのコレクションのモデルバインディングを行っていますが、そのような場合でもリフレクションを使って比較対象のクラスのプロパティを特定して検証可能です。ただ、リフレクションを使うということで、性能的にどうかという懸念は消せてませんが。

また、上のサンプルコードはサーバー側だけの検証で、クライアント側での JavaScript / jQuery による検証はかかりません。

単一のプロパティのカスタム検証であれば、@IT の記事の「第4回 検証属性の自作とクラス・レベルのモデル検証」のセクションに書かれているようにして、サーバー側とクライアント側両方の検証機能を実装できます。

しかし、今回のケースのように 2 つのプロパティにまたがった検証をする場合、スクリプトを ASP.NET MVC の検証システムの中に無理なく不整合なく取り込む具体例は少なくとも自分は見たことがないです。(自分が知らないだけだという可能性は否定しきれませんが)

Tags: ,

MVC

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  2019年1月  >>
303112345
6789101112
13141516171819
20212223242526
272829303112
3456789

View posts in large calendar