次の方法で共有


IDataErrorInfo インターフェイスの検証 (C#)

投稿者: Stephen Walther

Stephen Walther が、モデル クラスに IDataErrorInfo インターフェイスを実装して、カスタム検証エラー メッセージを表示する方法について説明します。

このチュートリアルの目的は、ASP.NET MVC アプリケーションで検証を実行するための 1 つのアプローチについて説明することです。 必須フォーム フィールドの値を指定せずに、他のユーザーが HTML フォームを送信できないようにする方法について説明します。 このチュートリアルでは、IErrorDataInfo インターフェイスを使用して検証を実行する方法について説明します。

前提条件

このチュートリアルでは、MoviesDB データベースと Movies データベース テーブルを使用します。 このテーブルには、次の列があります。

列名 [データ型] [NULL を許容]
Id int False
Title Nvarchar (100) False
監督 Nvarchar (100) False
DateReleased DateTime False

このチュートリアルでは、Microsoft Entity Framework を使用してデータベース モデル クラスを生成します。 Entity Framework によって生成された Movie クラスを図 1 に示します。

The Movie entity

図 01: Movie エンティティ (クリックすると、フルサイズの画像が表示されます)

Note

Entity Framework を使用してデータベース モデル クラスを生成する方法の詳細については、チュートリアル「Entity Framework でモデル クラスを作成する」を参照してください。

コントローラー クラス

Home コントローラーを使用してムービーを一覧表示し、新しいムービーを作成します。 このクラスのコードは、リスト 1 に含まれています。

リスト 1 - Controllers\HomeController.cs

using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;

namespace MvcApplication1.Controllers
{

    public class HomeController : Controller
    {
        private MoviesDBEntities _db = new MoviesDBEntities();

        public ActionResult Index()
        {
            return View(_db.MovieSet.ToList());
        }

        public ActionResult Create()
        {
            return View();
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create([Bind(Exclude = "Id")] Movie movieToCreate)
        {
            // Validate
            if (!ModelState.IsValid)
                return View();

            // Add to database
            try
            {
                _db.AddToMovieSet(movieToCreate);
                _db.SaveChanges();

                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

    }
}

リスト 1 の Home コントローラー クラスには、2 つの Create() アクションが含まれています。 最初のアクションでは、新しいムービーを作成するための HTML フォームが表示されます。 2 番目の Create() アクションは、新しいムービーをデータベースに実際に挿入します。 2 番目の Create() アクションは、最初の Create() アクションによって表示されるフォームがサーバーに送信されると呼び出されます。

2 番目の Create() アクションには、次のコード行が含まれていることに注目してください。

// Validate
if (!ModelState.IsValid)
    return View();

IsValid プロパティは、検証エラーが発生すると false を返します。 その場合、ムービーを作成するための HTML フォームを含む作成ビューが再表示されます。

部分クラスの作成

Movie クラスは Entity Framework によって生成されます。 ソリューション エクスプローラー ウィンドウで MoviesDBModel.edmx ファイルを展開し、コード エディターで MoviesDBModel.Designer.cs ファイルを開くと、Movie クラスのコードが表示されます (図 2 を参照)。

The code for the Movie entity

図 02: Movie エンティティのコード (クリックすると、フルサイズの画像が表示されます)

Movie クラスは部分クラスです。 つまり、同じ名前の別の部分クラスを追加して、Movie クラスの機能を拡張できます。 検証ロジックを新しい部分クラスに追加します。

リスト 2 のクラスを Models フォルダーに追加します。

リスト 2 - Models\Movie.cs

using System.Collections.Generic;
using System.ComponentModel;

namespace MvcApplication1.Models
{

    public partial class Movie 
    {

    }
}

リスト 2 のクラスに partial 修飾子が含まれていることに注目してください。 このクラスに追加するすべてのメソッドまたはプロパティは、Entity Framework によって生成された Movie クラスの一部になります。

OnChanging メソッドと OnChanged 部分メソッドの追加

Entity Framework がエンティティ クラスを生成すると、Entity Framework によって部分メソッドが自動的にクラスに追加されます。 Entity Framework は、クラスの各プロパティに対応する OnChanging および OnChanged 部分メソッドを生成します。

Movie クラスの場合、Entity Framework は次のメソッドを作成します。

  • OnIdChanging
  • OnIdChanged
  • OnTitleChanging
  • OnTitleChanged
  • OnDirectorChanging
  • OnDirectorChanged
  • OnDateReleasedChanging
  • OnDateReleasedChanged

OnChanging メソッドは、対応するプロパティが変更される直前に呼び出されます。 OnChanged メソッドは、プロパティが変更された直後に呼び出されます。

これらの部分メソッドを利用して、Movie クラスに検証ロジックを追加できます。 リスト 3 の更新プログラム Movie クラスは、Title と Director プロパティに値が割り当てられ、空でないことを確認します。

Note

部分メソッドは、実装が必須でないクラスで定義されたメソッドです。 部分メソッドを実装しない場合、コンパイラはメソッド シグネチャとメソッドのすべての呼び出しを削除し、部分メソッドに関連付けられた実行時のコストが発生しないようにします。 Visual Studio Code エディターでは、キーワード partial を入力し、その後にスペースを入力すると、実装する部分メソッドの一覧が表示され、そのようにして部分メソッドを追加できます。

リスト 3 - Models\Movie.cs

using System.Collections.Generic;
using System.ComponentModel;

namespace MvcApplication1.Models
{

    public partial class Movie : IDataErrorInfo
    {
        private Dictionary<string, string> _errors = new Dictionary<string, string>();

        partial void OnTitleChanging(string value)
        {
            if (value.Trim().Length == 0)
                _errors.Add("Title", "Title is required.");
        }

        partial void OnDirectorChanging(string value)
        {
            if (value.Trim().Length == 0)
                _errors.Add("Director", "Director is required.");
        }

    }
}

たとえば、Title プロパティに空の文字列を割り当てようとすると、エラー メッセージが _errors という名前のディクショナリに割り当てられます。

この時点で、Title プロパティに空の文字列を割り当て、プライベート _errors フィールドにエラーが追加されても、実際には何も起こりません。 これらの検証エラーを ASP.NET MVC フレームワークに公開するには、IDataErrorInfo インターフェイスを実装する必要があります。

IDataErrorInfo インターフェイスの実装

IDataErrorInfo インターフェイスは、.NET フレームワークの最初のバージョンから含まれています。 このインターフェイスは非常に単純なインターフェイスです。

public interface IDataErrorInfo
{
    string this[string columnName] { get; }

    string Error { get; }
}

クラスが IDataErrorInfo インターフェイスを実装する場合、ASP.NET MVC フレームワークは、クラスのインスタンスを作成するときにこのインターフェイスを使用します。 たとえば、Home コントローラーの Create() アクションは、Movie クラスのインスタンスを受け入れます。

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Movie movieToCreate)
{
    // Validate
    if (!ModelState.IsValid)
        return View();

    // Add to database
    try
    {
        _db.AddToMovieSet(movieToCreate);
        _db.SaveChanges();

        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

ASP.NET MVC フレームワークは、モデル バインダー (DefaultModelBinder) を使用して、Create() アクションに渡される Movie のインスタンスを作成します。 モデル バインダーは、HTML フォーム フィールドを Movie オブジェクトのインスタンスにバインドすることで、Movie オブジェクトのインスタンスを作成します。

DefaultModelBinder は、クラスが IDataErrorInfo インターフェイスを実装するかどうかを検出します。 クラスがこのインターフェイスを実装する場合、モデル バインダーは、クラスの各プロパティに対して IDataErrorInfo.this インデクサーを呼び出します。 インデクサーからエラー メッセージが返された場合、モデル バインダーはこのエラー メッセージをモデルの状態に自動的に追加します。

DefaultModelBinder は、IDataErrorInfo.Error プロパティもチェックします。 このプロパティは、クラスに関連付けられているプロパティ固有ではない検証エラーを表すことを目的としています。 たとえば、Movie クラスの複数のプロパティの値に依存する検証規則を適用できます。 その場合は、Error プロパティから検証エラーを返します。

リスト 4 の更新された Movie クラスは、IDataErrorInfo インターフェイスを実装します。

リスト 4 - Models\Movie.cs (IDataErrorInfo を実装)

using System.Collections.Generic;
using System.ComponentModel;

namespace MvcApplication1.Models
{

    public partial class Movie : IDataErrorInfo
    {
        private Dictionary<string, string> _errors = new Dictionary<string, string>();

        partial void OnTitleChanging(string value)
        {
            if (value.Trim().Length == 0)
                _errors.Add("Title", "Title is required.");
        }

        partial void OnDirectorChanging(string value)
        {
            if (value.Trim().Length == 0)
                _errors.Add("Director", "Director is required.");
        }

        #region IDataErrorInfo Members

        public string Error
        {
            get
            {
                return string.Empty;
            }
        }

        public string this[string columnName]
        {
            get
            {
                if (_errors.ContainsKey(columnName))
                    return _errors[columnName];
                return string.Empty;
            }
        }

        #endregion
    }
}

リスト 4 では、インデクサー プロパティは _errors コレクションをチェックし、インデクサーに渡されるプロパティ名に対応するキーが含まれているかどうかを確認します。 プロパティに関連付けられている検証エラーがない場合は、空の文字列が返されます。

変更した Movie クラスを使用するために Home コントローラーを変更する必要はありません。 図 3 に表示されるページは、[Title] または [Director] フォーム フィールドに値が入力されていない場合の動作を示しています。

Creating action methods automatically

図 03: 値が指定されていないフォーム (クリックすると、フルサイズの画像が表示されます)

DateReleased 値が自動的に検証されています。 DateReleased プロパティは NULL 値を受け入れないので、DefaultModelBinder は値がない場合にこのプロパティの検証エラーを自動的に生成します。 DateReleased プロパティのエラー メッセージを変更する場合は、カスタム モデル バインダーを作成する必要があります。

まとめ

このチュートリアルでは、IDataErrorInfo インターフェイスを使用して検証エラー メッセージを生成する方法について説明しました。 最初に、Entity Framework によって生成された Movie 部分クラスの機能を拡張する Movie 部分クラスを作成しました。 次に、Movie クラス OnTitleChanging() と OnDirectorChanging() 部分メソッドに検証ロジックを追加しました。 最後に、これらの検証メッセージを ASP.NET MVC フレームワークに公開するために、IDataErrorInfo インターフェイスを実装しました。