次の方法で共有


方法 : データ モデルでカスタム属性を使用してデータ フィールドの検証をカスタマイズする

更新 : 2008 年 7 月

ASP.NET Dynamic Data を使用すると、データ層レベルでデータ フィールドの検証をカスタマイズおよび拡張することができます。このトピックでは、次の操作を行うことで、データ モデルのデータ フィールドの検証を追加する方法について説明します。

  • カスタム検証属性を作成する。この属性により、データ モデルで検証のために使用するカスタム メタデータを作成できます。

  • カスタム検証属性を適用する。カスタム属性を作成した後で、検証するデータ フィールドに適用します。

この機能のオンライン サンプルを実行してください。

カスタム検証属性の作成

カスタム検証属性を使用すると、データ モデルでデータ フィールドの検証に使用できるメタデータを作成できます。カスタム属性は、ValidationAttribute 基本クラスから派生させる必要があります。

カスタム検証属性を作成するには

  1. ソリューション エクスプローラで、App_Code フォルダを右クリックし、[新しい項目の追加] をクリックします。

  2. [新しい項目の追加][クラス] をクリックします。

    [名前] ボックスに、カスタム検証属性クラスの名前を入力します。使用されていない任意の名前を使用できます。たとえば、CustomAttribute という名前のカスタム属性クラスを作成するためには、「CustomAttribute.cs」(Visual C# の場合) または「CustomAttribute.vb」(Visual Basic の場合) という名前を入力します。

  3. Imports キーワード (Visual Basic の場合) または using キーワード (Visual C# の場合) を使用して、System、System.Web.Globalization、および System.ComponentModel.DataAnnotations の各名前空間への参照を追加します。次に例を示します。

    using System;
    using System.Globalization;
    using System.ComponentModel.DataAnnotations;
    
    Imports System
    Imports System.Globalization
    Imports System.ComponentModel.DataAnnotations
    
  4. クラス定義を次のように変更します。

    • クラスを継承不可にします。NotInheritable キーワード (Visual Basic の場合) または sealed キーワード (Visual C# の場合) を追加します。

    • クラスを ValidationAttribute 基本データ型から派生させます。

    • AttributeUsageAttribute 属性をクラス定義に適用し、カスタム検証属性の使用方法を指定します。

    クラス定義の例を次に示します。AttributeUsageAttribute パラメータを設定して、カスタム検証属性がプロパティまたはフィールドに 1 回のみ適用されるようにしています。

    [AttributeUsage(AttributeTargets.Property | 
      AttributeTargets.Field, AllowMultiple = false)]
    sealed public class CustomAttribute : ValidationAttribute
    {
    
    }
    
    <AttributeUsage(AttributeTargets.[Property] Or _
        AttributeTargets.Field, AllowMultiple := False)> _
    Public NotInheritable Class CustomAttribute
        Inherits ValidationAttribute
    ....
    End Class
    
  5. IsValid メソッドをオーバーライドし、検証を実行するロジックを追加します。カスタム検証に成功した場合は true が返され、失敗した場合は false が返されます。検証対象の値は、唯一のパラメータとしてメソッドに渡されます。

    このメソッドのオーバーライドされたコードは次のようになります。

    public override bool IsValid(object value)
    {
      bool result = true;
      // Add validation logic here.
      return result;
    }
    
    Public Overrides Function IsValid( _
        ByVal value As Object) As Boolean
          ' Add validation logic here.
      Return result
    End Function
    
  6. 必要に応じて、FormatErrorMessage メソッドをオーバーライドしてカスタム エラー メッセージの書式設定を実行します。

    次の例は、検証に失敗したデータ フィールドの名前を使用してカスタム エラー メッセージを作成する方法を示しています。カスタム属性がデータ フィールドに適用されると、ErrorMessageString 値がパラメータとして渡されます。

    public override string FormatErrorMessage(string name)
    {
      return String.Format(CultureInfo.CurrentCulture, 
        ErrorMessageString, name);
    }
    
    Public Overrides Function FormatErrorMessage( _
        ByVal name As String) As String
          Return [String].Format(CultureInfo.CurrentCulture, _
            ErrorMessageString, name)
    End Function
    
  7. クラス属性ファイルを保存して閉じます。

カスタム検証属性の適用

データ フィールドの検証をカスタマイズするには、データ モデルを拡張する部分クラスを実装する必要があります。これにより、カスタム属性をデータ フィールドに適用できます。

検証用の部分クラスを作成するには

  1. ソリューション エクスプローラで、App_Code フォルダを右クリックし、[新しい項目の追加] をクリックします。

  2. [Visual Studio にインストールされたテンプレート][クラス] をクリックします。

    [名前] ボックスに、検証を追加するデータベース テーブルの名前を入力します。

    クラス名は、テーブルを表すエンティティ クラス名と一致させる必要があります。たとえば、Customers テーブルに検証を追加する場合は、ファイルに「Customer.cs」(Visual C# の場合) または「Customer.vb」(Visual Basic の場合) という名前を付け、クラス名を「Customer」にする必要があります。

  3. クラス定義に Partial キーワード (Visual Basic の場合) または partial キーワード (Visual C# の場合) を追加して部分クラスにします。

  4. Visual C# でクラスを作成する場合は、既定のコンストラクタを削除します。

    更新後のクラス宣言を次の例に示します。

    public partial class Customer {
    
    }
    
    Partial Public Class Customer
    
    End Class
    
  5. Imports キーワード (Visual Basic の場合) または using キーワード (Visual C# の場合) を使用して、System.Web.DynamicData 名前空間と System.ComponentModel.DataAnnotations 名前空間への参照を追加します。次に例を示します。

    using System.Web.DynamicData;
    using System.ComponentModel.DataAnnotations;
    
    Imports System.Web.DynamicData
    Imports System.ComponentModel.DataAnnotations
    
  6. 同じファイル内に、関連メタデータ クラスとして機能するクラスを追加します。このクラスには、クラス名として有効であり、まだ使用されていない名前であれば、任意の名前を付けることができます。

    メタデータのクラス宣言を次の例に示します。

    public class CustomerMetadata
    {
    
    }
    
    Public Class CustomerMetadata 
    
    End Class
    

    関連メタデータ クラスは、検証属性の適用先となるオブジェクトを提供します。

  7. 部分クラス定義に MetadataTypeAttribute 属性を適用します。属性のパラメータには、前の手順で作成した関連メタデータ クラスの名前を使用します。

    属性が適用された部分クラス定義を次の例に示します。

    [MetadataType(typeof(CustomerMetadata))]
    public partial class Customer {
    
    }
    
    <MetadataType(GetType(CustomerMetadata))> _
    Partial Public Class Customer
    
    End Class
    

これで、カスタム検証属性をデータ フィールドに適用できます。

カスタム検証属性をデータ フィールドに適用するには

  1. メタデータ クラスに、検証するデータ フィールドに対応のプロパティ (フィールド) を作成します。

  2. 前に作成したカスタム検証属性を、検証するデータ フィールドに適用します。

    次の例は、カスタム検証属性を Phone データ フィールドに適用する方法を示しています。

    public partial class CustomerMetadata
    {
      [CustomAttribute(parm1,
        ErrorMessage = "{0} field validation failed.")]
      public object Phone; 
    }
    
    Public Class CustomerMetadata 
      <PhoneMask(parm1, _
        ErrorMessage:="{0} field validation failed.")> _
      Public Phone As Object
    End Class
    
  3. クラス ファイルを保存して閉じます。

使用例

次の例は、PhoneMaskAttribute という名前のカスタム属性を作成して、AdventureWorksLT データベースの Customer テーブルの Phone データ フィールドに適用する方法を示しています。この例では、データ モデルに LINQ-to-SQL クラスを使用します。

属性により、特定の電話番号形式を表すマスクに対して Phone データ フィールドを検証するように Dynamic Data に指示しています。ユーザーの入力した電話番号がマスクに一致しない場合は、属性のコードからカスタム エラーが発行されます。

Imports Microsoft.VisualBasic
Imports System
Imports System.Globalization
Imports System.ComponentModel.DataAnnotations

<AttributeUsage(AttributeTargets.[Property] Or AttributeTargets.Field, AllowMultiple:=False)> _
Public NotInheritable Class PhoneMaskAttribute
    Inherits ValidationAttribute

    ' Internal field to hold the mask value.
    ReadOnly _mask As String
    Public ReadOnly Property Mask() As String
        Get
            Return _mask
        End Get
    End Property

    Public Sub New(ByVal mask As String)
        _mask = mask
    End Sub

    Public Overrides Function IsValid( _
    ByVal value As Object) As Boolean
        Dim phoneNumber As String = DirectCast(value, String)
        Dim result As Boolean = True
        If Me.Mask <> Nothing Then
            result = MatchesMask(Me.Mask, phoneNumber)
        End If
        Return result
    End Function

    ' Checks if the entered phone number matches the mask.
    Public Shared Function MatchesMask(ByVal mask As String, _
        ByVal phoneNumber As String) As Boolean
        If mask.Length <> phoneNumber.Trim().Length Then
            ' Length mismatch.
            Return False
        End If
        Dim i As Integer = 0
        While i < mask.Length
            If mask(i) = "d"c _
             AndAlso Char.IsDigit(phoneNumber(i)) = False Then
                ' Digit expected at this position.      
                Return False
            End If
            If mask(i) = "-"c AndAlso phoneNumber(i) <> "-"c Then
                ' Spacing character expected at this position.
                Return False
            End If
            System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)
        End While
        Return True
    End Function

    Public Overrides Function FormatErrorMessage( _
    ByVal name As String) As String
        Return [String].Format(CultureInfo.CurrentCulture, _
          ErrorMessageString, name, Me.Mask)
    End Function


End Class

using System;
using System.Globalization;
using System.ComponentModel.DataAnnotations;


[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class PhoneMaskAttribute : ValidationAttribute
{
    // Internal field to hold the mask value.
    readonly string _mask;

    public string Mask
    {
        get { return _mask; }
    }

    public PhoneMaskAttribute(string mask)
    {
        _mask = mask;
    }


    public override bool IsValid(object value)
    {
        var phoneNumber = (String)value;
        bool result = true;
        if (this.Mask != null)
        {
            result = MatchesMask(this.Mask, phoneNumber);
        }
        return result;
    }

    // Checks if the entered phone number matches the mask.
    internal bool MatchesMask(string mask, string phoneNumber)
    {
        if (mask.Length != phoneNumber.Trim().Length)
        {
            // Length mismatch.
            return false;
        }
        for (int i = 0; i < mask.Length; i++)
        {
            if (mask[i] == 'd' && char.IsDigit(phoneNumber[i]) == false)
            {
                // Digit expected at this position.
                return false;
            }
            if (mask[i] == '-' && phoneNumber[i] != '-')
            {
                // Spacing character expected at this position.
                return false;
            }
        }
        return true;
    }

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

}
Imports Microsoft.VisualBasic
Imports System.Web.DynamicData
Imports System.ComponentModel.DataAnnotations

<MetadataType(GetType(CustomerMetadata))> _
Partial Public Class Customer

End Class

Public Class CustomerMetadata
    <PhoneMask("999-999-9999", _
    ErrorMessage:="{0} field value does not match the mask {1}.")> _
  Public Phone As Object

End Class
using System.Web.DynamicData;
using System.ComponentModel.DataAnnotations;

[MetadataType(typeof(CustomerMetadata))]
public partial class Customer
{

}

public class CustomerMetadata
{
    [PhoneMask("999-999-9999",
        ErrorMessage = "{0} value does not match the mask {1}.")]
    public object Phone; 
}
<%@ Page Language="VB" 
AutoEventWireup="true" CodeFile="CustomAttributeValidation.aspx.vb" 
Inherits="CustomAttributeValidation" %>


<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
    <link href="~/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
     <h2>Example: <%=Title%></h2>

     <!-- Enable dynamic behavior. The GridView must be 
     registered with the manager. See code-behind file. -->
    <asp:DynamicDataManager ID="DynamicDataManager1" runat="server"
        AutoLoadForeignKeys="true" />


    <form id="form1" runat="server">

        <!-- Capture validation exceptions -->
        <asp:DynamicValidator ID="ValidatorID" ControlToValidate="GridView1" 
            runat="server" /> 

        <asp:GridView ID="GridView1" 
            runat="server" 
            DataSourceID="GridDataSource" 
            AutoGenerateColumns="false"  
            AutoGenerateEditButton="true"
            AllowPaging="true"
            PageSize="10"
            AllowSorting="true">
            <Columns>
                <asp:DynamicField DataField="FirstName" />
                <asp:DynamicField DataField="LastName" />
                <asp:DynamicField DataField="Phone" />
            </Columns>
       </asp:GridView>
    </form>

    <!-- Connect to the database -->
    <asp:LinqDataSource ID="GridDataSource" runat="server"  
        TableName="Customers" EnableUpdate="true"
        ContextTypeName="AdventureWorksLTDataContext">
    </asp:LinqDataSource>
</body>
</html>
<%@ Page Language="C#" 
AutoEventWireup="true" CodeFile="CustomAttributeValidation.aspx.cs" 
Inherits="CustomAttributeValidation" %>


<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
    <link href="~/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
     <h2>Example: <%=Title%></h2>

     <!-- Enable dynamic behavior. The GridView must be 
     registered with the manager. See code-behind file. -->
    <asp:DynamicDataManager ID="DynamicDataManager1" runat="server"
        AutoLoadForeignKeys="true" />


    <form id="form1" runat="server">

        <!-- Capture validation exceptions -->
        <asp:DynamicValidator ID="ValidatorID" ControlToValidate="GridView1" 
            runat="server" /> 

        <asp:GridView ID="GridView1" 
            runat="server" 
            DataSourceID="GridDataSource" 
            AutoGenerateColumns="false"  
            AutoGenerateEditButton="true"
            AllowPaging="true"
            PageSize="10"
            AllowSorting="true">
            <Columns>
                <asp:DynamicField DataField="FirstName" />
                <asp:DynamicField DataField="LastName" />
                <asp:DynamicField DataField="Phone" />
            </Columns>
       </asp:GridView>
    </form>

    <!-- Connect to the database -->
    <asp:LinqDataSource ID="GridDataSource" runat="server"  
        TableName="Customers" EnableUpdate="true"
        ContextTypeName="AdventureWorksLTDataContext">
    </asp:LinqDataSource>
</body>
</html>
Imports System
Imports System.Collections
Imports System.Configuration
Imports System.Web.DynamicData

Partial Public Class CustomAttributeValidation
    Inherits System.Web.UI.Page
    Protected _table As MetaTable

    Protected Sub Page_Init(ByVal sender As Object, ByVal e As EventArgs)
        ' Register control with the data manager.
        DynamicDataManager1.RegisterControl(GridView1)
    End Sub

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
        ' Get the table entity.
        _table = GridDataSource.GetTable()

        ' Assign title dynamically.
        Title = String.Concat( _
        "Customize <i>Phone</i> Data Field Validation", _
        "Using a Custom Attribute")
    End Sub
End Class
using System;
using System.Collections;
using System.Configuration;
using System.Web.DynamicData;

public partial class CustomAttributeValidation : System.Web.UI.Page
{
    protected MetaTable _table;

    protected void Page_Init(object sender, EventArgs e)
    {
        // Register control with the data manager.
        DynamicDataManager1.RegisterControl(GridView1);
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        // Get the table entity.
        _table = GridDataSource.GetTable();

        // Assign title dynamically.
        Title = string.Concat("Customize <i>Phone</i> Data Field Validation",
            "Using a Custom Attribute");

    }
}

コードのコンパイル方法

このコード例をコンパイルするには、以下が必要です。

  • Microsoft Visual Studio 2008 Service Pack 1 または Visual Web Developer 2008 Express Edition Service Pack 1。

  • AdventureWorksLT サンプル データベース。SQL Server のサンプル データベースをダウンロードしてインストールする方法については、CodePlex サイトの「Microsoft SQL Server Product Samples: Database」を参照してください。実行している SQL Server のバージョン (Microsoft SQL Server 2005 または Microsoft SQL Server 2008) に対応した正しいバージョンのサンプル データベースをインストールしてください。

  • 動的なデータ ドリブン Web サイトの作成。これにより、データベースのデータ コンテキストと、カスタマイズするデータ フィールドおよびオーバーライドするメソッドを持つクラスを作成できます。詳細については、「Walkthrough: Creating a New Dynamic Data Web Site using Scaffolding」を参照してください。

参照

概念

ASP.NET Dynamic Data のフィールド テンプレートの概要

ASP.NET Dynamic Data モデルの概要

ASP.NET Dynamic Data の概要

参照

ValidationAttribute

DynamicValidator

部分クラスと部分メソッド (C# プログラミング ガイド)

履歴の変更

日付

履歴

理由

2008 年 7 月

トピックを追加

SP1 機能変更