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

作成者: 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.vb

Public Class HomeController
Inherits Controller

Private _db As New MoviesDBEntities()

Public Function Index() As ActionResult
    Return View(_db.MovieSet.ToList())
End Function

Public Function Create() As ActionResult
    Return View()
End Function

<AcceptVerbs(HttpVerbs.Post)> _
Public Function Create(<Bind(Exclude := "Id")> ByVal movieToCreate As Movie) As ActionResult
        ' Validate
        If (Not ModelState.IsValid) Then
        Return View()
        End If

    ' Add to database
    Try
        _db.AddToMovieSet(movieToCreate)
        _db.SaveChanges()

        Return RedirectToAction("Index")
    Catch
        Return View()
    End Try
End Function

End Class

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

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

' Validate
If (Not ModelState.IsValid) Then
Return View()
End If

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

部分クラスの作成

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

The code for the Movie entity

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

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

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

リスト 2 - Models\Movie.vb

Public Partial Class Movie

End Class

リスト 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.vb

Imports System.ComponentModel

Partial Public Class Movie
    Implements IDataErrorInfo

    Private _errors As New Dictionary(Of String, String)()

    Private Sub OnTitleChanging(ByVal value As String)
        If value.Trim().Length = 0 Then
            _errors.Add("Title", "Title is required.")
        End If
    End Sub

    Private Sub OnDirectorChanging(ByVal value As String)
        If value.Trim().Length = 0 Then
            _errors.Add("Director", "Director is required.")
        End If
    End Sub

End Class

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

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

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

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

Public Interface IDataErrorInfo
  Default ReadOnly Property Item(ByVal columnName As String) As String
  ReadOnly Property [Error]() As String
End Interface

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

<AcceptVerbs(HttpVerbs.Post)> _
Public Function Create(<Bind(Exclude := "Id")> ByVal movieToCreate As Movie) As ActionResult
    ' Validate
    If (Not ModelState.IsValid) Then
        Return View()
    End If

    ' Add to database
    Try
        _db.AddToMovieSet(movieToCreate)
        _db.SaveChanges()

        Return RedirectToAction("Index")
    Catch
        Return View()
    End Try
End Function

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

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

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

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

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

Imports System.ComponentModel

Partial Public Class Movie
    Implements IDataErrorInfo

    Private _errors As New Dictionary(Of String, String)()

    Private Sub OnTitleChanging(ByVal value As String)
        If value.Trim().Length = 0 Then
            _errors.Add("Title", "Title is required.")
        End If
    End Sub

    Private Sub OnDirectorChanging(ByVal value As String)
        If value.Trim().Length = 0 Then
            _errors.Add("Director", "Director is required.")
        End If
    End Sub

#Region "IDataErrorInfo Members"

    Public ReadOnly Property [Error]() As String Implements IDataErrorInfo.Error
        Get
            Return String.Empty
        End Get
    End Property

    Default Public ReadOnly Property Item(ByVal columnName As String) As String Implements IDataErrorInfo.Item
        Get
            If _errors.ContainsKey(columnName) Then
                Return _errors(columnName)
            End If
            Return String.Empty
        End Get
    End Property

#End Region

End Class

リスト 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 インターフェイスを実装しました。