共用方式為


HOW TO:使用自訂屬性自訂資料模型中的資料欄位驗證

更新: 2008 年 7 月

ASP.NET 動態資料可讓您在資料層自訂和擴充資料欄位驗證。本主題將藉由執行下列步驟,說明如何在資料模型中加入資料欄位驗證:

  • 建立自訂驗證屬性。這個屬性可讓您建立用於資料模型中進行驗證的自訂中繼資料 (Metadata)。

  • 套用自訂驗證屬性。建立中繼資料屬性後,您可將其套用至要驗證的資料欄位。

執行此功能的線上範例。

建立自訂驗證屬性

自訂驗證屬性可讓您建立在資料模型中用來驗證資料欄位的中繼資料。您必須從 ValidationAttribute 基底類別 (Base Class) 衍生自訂屬性。

若要建立自訂驗證屬性

  1. 在 [方案總管] 中,以滑鼠右鍵按一下 [App_Code] 資料夾,然後按一下 [加入新項目]。

  2. 按一下 [加入新項目] 下方的 [類別]。

    在 [名稱] 方塊中,輸入自訂驗證屬性類別的名稱。任何尚未使用的名稱都可以使用。例如,您可以輸入 CustomAttribute.vb (適用於 Visual C#) 或輸入 CustomAttribute.vb (適用於 Visual Basic),以建立名為 CustomAttribute 的自訂屬性類別。

  3. 使用 Visual Basic 中的 Imports 關鍵字或 Visual C# 中的 using 關鍵字,加入對 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 基底型別 (Base Type) 衍生類別。

    • AttributeUsageAttribute 屬性套用至類別定義,陳述自訂驗證屬性的使用方式。

    下列範例說明類別定義。同時也會設定 AttributeUsageAttribute 參數,讓自訂驗證屬性 (Attribute) 只能套用至屬性 (Property) 或欄位一次。

    [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. 在 Visual Basic 中將 Partial 關鍵字加入類別定義,或在 Visual C# 中將 partial 關鍵字加入類別定義,使其成為部分類別。 

  4. 如果要在 Visual C# 中建立類別,請先刪除預設的建構函式。

    在下列範例中,會示範更新的類別宣告。

    public partial class Customer {
    
    }
    
    Partial Public Class Customer
    
    End Class
    
  5. 使用 Imports 關鍵字 (適用於 Visual Basic) 或 using 關鍵字 (適用於 Visual C#),加入 System.Web.DynamicDataSystem.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 資料欄位。如果使用者輸入的電話號碼不符合遮罩,則該屬性程式碼會發出自訂錯誤。

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" >
    <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" 
        AutoLoadForeignKeys="true" />


    <form id="form1" >

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

        <asp:GridView ID="GridView1" 
             
            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"   
        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" >
    <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" 
        AutoLoadForeignKeys="true" />


    <form id="form1" >

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

        <asp:GridView ID="GridView1" 
             
            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"   
        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 版 Service Pack 1。 

  • AdventureWorksLT 範例資料庫。如需下載並安裝 SQL Server 範例資料庫,請參閱 CodePlex 網站上的 Microsoft SQL Server 產品範例:資料庫 (英文)。請確認已配合您所執行的 SQL Server 版本 (Microsoft SQL Server 2005 或 Microsoft SQL Server 2008) 安裝版本正確的範例資料庫。

  • 動態資料驅動型網站。這樣可讓您為資料庫建立資料內容,以及建立包含要自訂的資料欄位類別和要覆寫的方法。如需詳細資訊,請參閱Walkthrough: Creating a New Dynamic Data Web Site using Scaffolding

請參閱

概念

ASP.NET 動態資料欄位範本概觀

ASP.NET 動態資料模型概觀

ASP.NET 動態資料概觀

參考

ValidationAttribute

DynamicValidator

部分類別和方法 (C# 程式設計手冊)

變更記錄

日期

記錄

原因

2008 年 7 月

加入主題。

SP1 功能變更。