How to: Customize Data Field Validation in the Data Model

ASP.NET Dynamic Data enables you to customize and extend data validation to the data model. This topic shows how you can add data field validation in the data model in the following ways:

  • Customize validation for individual data fields by applying Dynamic Data System.ComponentModel.DataAnnotations attributes to the fields. These attributes define common validation patterns, such as range checking and required fields. This approach lets you use predefined validation checks with very little coding. You should use this approach when you want to apply additional validation to what is already provided by Dynamic Data, and the default System.ComponentModel.DataAnnotations attributes are sufficient for your requirements.

    Note

    You can also create custom validation attributes. This lets you expand the validation that is provided by the System.ComponentModel.DataAnnotations attributes. This approach is useful if the available attributes do not meet the validation requirements for a specific data field. For more information, see How to: Customize Data Field Validation in the Data Model Using Custom Attributes.

  • Customize validation for an individual data field by overriding the partial-class method that processes changes for that data field (or by handling a corresponding event). This approach lets you add validation and business logic for an individual field.

  • Customize validation for any data field by overriding the OnValidate method (or handling the Validate event). This method is called when any data field in the table is being processed. This approach is more general than adding validation for an individual field. It is useful when the same validation logic can be applied to more than one data field. It also lets you perform validation checks that involve multiple fields.

    Note

    There are slight differences in how to implement custom validation depending on whether the data model is based on LINQ-to-SQL classes or on ADO.NET Entity Framework classes. This topic includes procedures that are specific to each data model.

Any validation exceptions that are thrown in the data model are caught by the DynamicValidator control. If a page contains a DynamicValidator control, the error can be displayed in the page.

Run an online example of this feature: Run.

Creating a Partial Class for Validation

Before you can customize validation at the data-model layer, you must implement a partial class that extends the data model. This enables you to do the following:

  • Customize validation by adding metadata information through attributes.

  • Customize validation by implementing partial-class methods that enable you to create your own validation logic.

To create partial class for validation

  1. In Solution Explorer, right-click the App_Code folder, and then click Add New Item.

  2. Under Visual Studio installed templates, click Class.

    In the Name box, enter the name of the data table that you want to add validation for.

    The class name must match the entity class name that represents the table. For example, to add validation for the Customers table, enter "Customer" and name the class Customer.

  3. Add the Partial keyword in Visual Basic or the partial keyword in Visual C# to the class definition to make it a partial class. 

    The following example shows the updated class declaration.

    public partial class Customer {
    }
    
    Partial Public Class Customer
    
    End Class
    
  4. If you are creating the class in Visual C#, delete the default constructor.

  5. Add references to the System.Web.DynamicData and System.ComponentModel.DataAnnotations namespaces by using the Imports keyword in Visual Basic or the using keyword in Visual C#, as shown in the following example:

    using System.Web.DynamicData;
    using System.ComponentModel.DataAnnotations;
    
    Imports System.Web.DynamicData
    Imports System.ComponentModel.DataAnnotations
    
  6. In the same file, create a second class that will act as the associated metadata class. You can use any name for the class that is a valid class name and that is not already being used.

    The following example shows a metadata class declaration.

    [C#]

    public class CustomerMetadata {
    }
    
    Public Class CustomerMetadata 
    
    End Class
    

    The associated metadata class provides an object that you can apply validation attributes to.

  7. Apply the MetadataTypeAttribute attribute to the partial-class definition. For the attribute's parameter, use the name of the associated metadata class that you created in the previous step.

    The following example shows the partial-class definition with the attributed added.

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

Customizing Validation by Using Attributes

This section shows how to customize validation by using default validation rules that are provided by Dynamic Data System.ComponentModel.DataAnnotations attributes.

To validate a specific data field by using validation attributes

  1. In the metadata class, create a property or field whose name corresponds to the data field to validate

  2. Apply one of the attributes in the System.ComponentModel.DataAnnotations namespace to the property.

    The following example shows how to apply the System.ComponentModel.DataAnnotations..::.RequiredAttribute attribute to the Title data field in the associated metadata class. If a user enters an empty string, the IsValid method throws a validation exception and generates an error message.

    Note

    If you apply the RequiredAttribute attribute, you require users to enter a value even if this is not required by the database.

    public class CustomerMetadata
    {
        [Required()]
        public object Title;
    }
    
    Public Class CustomerMetadata 
        <Required()> _
        Public Title As Object
    End Class
    

Customizing Validation for an Individual Data Field by Using a Partial-Class Method

This section shows how to customize validation by overriding a partial-class method that processes the changes that are made to an individual data field. This type of validation enables you to create your own rules to perform validation instead of relying on the built-in Dynamic Data validation checks that are implemented in the System.ComponentModel.DataAnnotations attributes.

To validate a specific data field by using a partial-class method

  1. Override the partial-class method that processes the changes that were made to the data field.

  2. Add custom validation logic.

    The following example shows how to override the OnTitleChanging method in a partial Customer class. The method is called when Title field of the Customer data table is being changed. The code in the example checks that the new title entered by the user starts with an uppercase letter. If the data does not pass validation, the method throws an exception. The value to be validated is passed to the method as the only parameter. The parameter is typed to match the data type of the data to validate.

    Note

    Any validation exceptions that are thrown in the data model are caught by the DynamicValidator control. If a page contains a DynamicValidator control, the error can be displayed in the page.

    public partial class Customer 
    {
        partial void OnTitleChanging(string value) 
        {
            if (!Char.IsUpper(value[0])) {
              throw new ValidationException(
               "Title must start with an uppercase letter.");}
            }
    }
    
    Public Partial Class Customer
        Private Sub OnTitleChanging(ByVal value As String)
            If Not [Char].IsUpper(value(0)) Then
                Throw New ValidationException( _
                    "Title must start with an uppercase letter.")
            End If
        End Sub
    End Class
    

Customizing Validation for All Data Fields by Using a Partial-Class Method in the LINQ-to-SQL Data Model

This section shows how to customize validation by overriding the partial-class method that processes the changes that are made to any data field in a table.

This type of validation enables you to create your own rules to perform validation instead of relying on the built-in Dynamic Data validation checks that are implemented in the System.ComponentModel.DataAnnotations attributes. This approach is useful when the same validation logic can be applied to more than one data field. It also lets you perform validation checks that involve multiple fields.

Note

The instructions in this section apply if you are using the LINQ-to-SQL data model. For information about how to perform a similar task if you are using the ADO.NET Entity Framework data model, see the next section.

To validate any data field by using a partial-class method in the LINQ-to-SQL data model

  1. Override the OnValidate partial-class method that is invoked when changes are made to any data field in the table.

  2. Add custom validation logic.

    The following example shows how to override the OnValidate method. This code checks that the first name and last name that are entered by the user start with an uppercase letter. If the data does not pass validation, the method throws an exception.

    Note

    Any validation exceptions that are thrown in the data model are caught by the DynamicValidator control. If a page contains a DynamicValidator control, the error can be displayed in the page.

    partial void OnValidate(System.Data.Linq.ChangeAction action)
    {
        if (!Char.IsUpper(this._LastName[0]) || 
                !Char.IsUpper(this._FirstName[0]))
            throw new ValidationException(
                "Name must start with an uppercase letter.");
    }
    
    Private Sub OnValidate(ByVal action As _ 
           System.Data.Linq.ChangeAction)
        If Not [Char].IsUpper(Me._LastName(0)) OrElse _
                Not [Char].IsUpper(Me._FirstName(0)) Then
            Throw New ValidationException( _
                "Name must start with an uppercase letter.")
        End If
    End Sub
    

Customizing Validation for All Data Fields by Using a Partial-Class Method in the ADO.NET Entity Framework

This section shows how to customize validation by creating an event handler in the ADO.NET Entity class. This approach is useful when the same validation logic can be applied to more than one data field or when you perform validation checks that involve multiple fields that must be consistent as a group.

To validate any data field by using an event handler

  1. Create a partial class for the ADO.NET Entity class that you want to validate.

    For example, if you are using the AdventureWorksLT database, create a partial class named AdventureWorksLTEntities inside the AdventureWorksLTModel namespace.

  2. If you are working in C#, create a partial OnContextCreated method to register an event handler for the SavingChanges event.

    The following example shows the OnContextCreated method in which the SavingChanges event is registered.

    namespace AdventureWorksLTModel {
        public partial class AdventureWorksLTEntities {
            partial void OnContextCreated() {
                this.SavingChanges += new 
                    System.EventHandler(OnSavingChanges);
            }
    
            public void OnSavingChanges(object sender, System.EventArgs e) {
            }
        }
    }
    
  3. If you are using Visual Basic, create an event handler for the SavingChanges event.

    The following example shows a OnSavingChanges method that is called when a table is updated and when the SavingChanges event is raised.

    Namespace AdventureWorksLTModel
        Partial Public Class AdventureWorksLTEntities
            Sub OnSavingChanges(ByVal sender As Object, _
                  ByVal e As System.EventArgs) _
                  Handles Me.SavingChanges
    
            End Sub
        End Class
    End Namespace
    
  4. Add custom validation logic to the partial-class method.

    The ObjectStateManager class maintains the state of the entity class. The following example shows how to enumerate the modified entities in the object state manager. The code then verifies that the sell start date, sell end date, and discontinued date are after today. If any of the dates are not after today, a validation exception is thrown.

    Note

    Any validation exceptions that are thrown in the data model are caught by the DynamicValidator control. The page templates included with a Dynamic Data project contain a DynamicValidator control, which displays the validations errors on the page.

    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Web.DynamicData;
    using System;
    using System.Data;
    using System.Data.Objects;
    
    namespace AdventureWorksLTModel {
        public partial class AdventureWorksLTEntities {
            partial void OnContextCreated() {
                this.SavingChanges += 
                    new System.EventHandler(OnSavingChanges);
            }
    
            public void OnSavingChanges(object sender, 
                    System.EventArgs e) {
                var stateManager = 
                    ((AdventureWorksLTEntities)sender).ObjectStateManager;
                var changedEntities = 
                    ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
    
                foreach (ObjectStateEntry stateEntryEntity in 
                        changedEntities) {
                    if (stateEntryEntity.Entity is Product) {
                        Product prod = (Product)stateEntryEntity.Entity;
                        rValidateDate(prod.SellStartDate);
                        rValidateDate(prod.SellEndDate);
                        rValidateDate(prod.DiscontinuedDate);
                    }
                }
            }
    
            void rValidateDate(global::System.Nullable<global::System.DateTime> value) {
                DateTime dt = (DateTime)value;
                if (dt.CompareTo(DateTime.Now) < 0)
                    throw new ValidationException("Dates must be after today.");
            }
        }
    }
    
    Imports Microsoft.VisualBasic
    Imports System.ComponentModel.DataAnnotations ' ValidationException
    Imports System.Data        ' EntityState
    
    Namespace AdventureWorksLTModel
        Partial Public Class AdventureWorksLTEntities
    
            Sub OnSavingChanges(ByVal sender As Object, _
                  ByVal e As System.EventArgs) _
                  Handles Me.SavingChanges
    
                Dim changedEntities = Me.ObjectStateManager. _
                GetObjectStateEntries(EntityState.Modified)
    
                For Each stateEntryEntity In changedEntities
                    If TypeOf stateEntryEntity.Entity Is Product Then
                        Dim prod As Product = _
                           CType(stateEntryEntity.Entity, Product)
                        rValidateDate(prod.SellEndDate)
                        rValidateDate(prod.SellStartDate)
                        rValidateDate(prod.DiscontinuedDate)
                    End If
                Next
            End Sub
    
            Sub rValidateDate(ByVal aDate As Nullable(Of DateTime))
                If (aDate Is Nothing) Then
                    Return
                End If
                Dim a2 As DateTime = aDate
                If (a2.CompareTo(DateTime.Now) < 0) Then
                    Throw New ValidationException( _ 
                        "Dates must be later than today.")
                End If
            End Sub
        End Class
    End Namespace
    

Example

The example shows how to use the RequiredAttribute attribute to validate data for the Title field of the Customer table. It uses the OnValidate partial-class method to make sure that the values that are entered by the user for the Title, FirstName, and LastName data fields start with an uppercase letter. The example also uses the OnOrderQtyChanging partial-class method to make sure that the value entered by the user for the OrderQty data field of the SalesOrderDetails table is greater than a specified minimum.

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.DynamicData
Imports System.ComponentModel.DataAnnotations

<MetadataType(GetType(CustomerMetadata))> _
PartialPublicClass Customer


    PrivateSub OnValidate(ByVal action As System.Data.Linq.ChangeAction)
        IfNotChar.IsUpper(Me._LastName(0)) _
        OrElseNotChar.IsUpper(Me._FirstName(0)) _
        OrElseNotChar.IsUpper(Me._Title(0)) ThenThrowNew ValidationException( _
               "Data value must start with an uppercase letter.")
        EndIfEndSubEndClassPublicClass CustomerMetadata
    <Required()> _
    Public Title AsObjectEndClass
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.DynamicData;
using System.ComponentModel.DataAnnotations;

[MetadataType(typeof(CustomerMetadata))]
publicpartialclass Customer
{


    partialvoid OnValidate(System.Data.Linq.ChangeAction action)
    {
        if (!char.IsUpper(this._LastName[0]) ||
            !char.IsUpper(this._FirstName[0])  ||
            !char.IsUpper(this._Title[0]))
            thrownew ValidationException(
               "Data value must start with an uppercase letter.");
    }


}

publicclass CustomerMetadata
{
    [Required()]
    publicobject Title;

}
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.DynamicData
Imports System.ComponentModel.DataAnnotations

PartialPublicClass SalesOrderDetail
    PrivateSub OnOrderQtyChanging(ByVal value AsShort)
        If value < 100 ThenThrowNew ValidationException( _
               "Quantity is less than the allowed minimum of 100.")
        EndIfEndSubEndClass
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.DynamicData;
using System.ComponentModel.DataAnnotations;

publicpartialclass SalesOrderDetail
{
    partialvoid OnOrderQtyChanging(shortvalue)
    {
        if (value < 100)
        {
            thrownew ValidationException(
                "Quantity is less than the allowed minimum of 100.");
        }
    }
}

<%@ Page Language="VB" 
AutoEventWireup="true" CodeFile="CustomValidation.aspx.vb"Inherits="CustomValidation" %>


<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:DynamicValidator ID="DynamicValidator1" ControlToValidate="GridView2" 
            runat="server" /> 
        <table>
            <tr>
                <td align="left" valign="top" style="font-weight:bold">
                    Customize Validation Using the Table OnValidate 
                </td>
                <td>
                    <asp:GridView ID="GridView1" 
                        runat="server" 
                        DataSourceID="GridDataSource" 
                        AutoGenerateColumns="false"  
                        AutoGenerateEditButton="true"
                        AllowPaging="true" 
                        PageSize="5"
                        AllowSorting="true">
                        <Columns>
                            <asp:DynamicField DataField="Title" />
                            <asp:DynamicField DataField="FirstName" />
                            <asp:DynamicField DataField="LastName" />
                        </Columns>
                        <EmptyDataTemplate>
                            There are currently no items in this table.
                        </EmptyDataTemplate>
                    </asp:GridView>
                </td>
            </tr>
            <tr>
                <td align="left" valign="top" style="font-weight:bold">
                    Customize Validation Using OnOrderQtyChanging
                </td>
                <td>
                    <asp:GridView ID="GridView2" 
                        runat="server" 
                        DataSourceID="GridDataSource2" 
                        AutoGenerateColumns="false"  
                        AutoGenerateEditButton="true"
                        AllowPaging="true" 
                        PageSize="5"
                        AllowSorting="true">
                        <Columns>
                            <asp:DynamicField DataField="OrderQty" />
                        </Columns>
                        <EmptyDataTemplate>
                            There are currently no items in this table.
                        </EmptyDataTemplate>
                    </asp:GridView>
                </td>
            </tr>
        </table>

    </form>

    <!-- Connect to the database -->
    <asp:LinqDataSource ID="GridDataSource" runat="server"  
         TableName="Customers" EnableUpdate="true"
        ContextTypeName="AdventureWorksLTDataContext">

    </asp:LinqDataSource>

     <!-- Connect to the database -->
    <asp:LinqDataSource ID="GridDataSource2" runat="server"  
         TableName="SalesOrderDetails" EnableUpdate="true"
        ContextTypeName="AdventureWorksLTDataContext">

    </asp:LinqDataSource>
</body>
</html>
<%@ Page Language="C#" 
AutoEventWireup="true" CodeFile="CustomValidation.aspx.cs" 
Inherits="CustomValidation" %>


<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:DynamicValidator ID="DynamicValidator1" ControlToValidate="GridView2" 
            runat="server" /> 
        <table>
            <tr>
                <td align="left" valign="top" style="font-weight:bold">
                    Customize Validation Using the Table OnValidate 
                </td>
                <td>
                    <asp:GridView ID="GridView1" 
                        runat="server" 
                        DataSourceID="GridDataSource" 
                        AutoGenerateColumns="false"  
                        AutoGenerateEditButton="true"
                        AllowPaging="true" 
                        PageSize="5"
                        AllowSorting="true">
                        <Columns>
                            <asp:DynamicField DataField="Title" />
                            <asp:DynamicField DataField="FirstName" />
                            <asp:DynamicField DataField="LastName" />
                        </Columns>
                        <EmptyDataTemplate>
                            There are currently no items inthis table.
                        </EmptyDataTemplate>
                    </asp:GridView>
                </td>
            </tr>
            <tr>
                <td align="left" valign="top" style="font-weight:bold">
                    Customize Validation Using OnOrderQtyChanging
                </td>
                <td>
                    <asp:GridView ID="GridView2" 
                        runat="server" 
                        DataSourceID="GridDataSource2" 
                        AutoGenerateColumns="false"  
                        AutoGenerateEditButton="true"
                        AllowPaging="true" 
                        PageSize="5"
                        AllowSorting="true">
                        <Columns>
                            <asp:DynamicField DataField="OrderQty" />
                        </Columns>
                        <EmptyDataTemplate>
                            There are currently no items inthis table.
                        </EmptyDataTemplate>
                    </asp:GridView>
                </td>
            </tr>
        </table>

    </form>

    <!-- Connect to the database -->
    <asp:LinqDataSource ID="GridDataSource" runat="server"  
         TableName="Customers" EnableUpdate="true"
        ContextTypeName="AdventureWorksLTDataContext">

    </asp:LinqDataSource>

     <!-- Connect to the database -->
    <asp:LinqDataSource ID="GridDataSource2" runat="server"  
         TableName="SalesOrderDetails" EnableUpdate="true"
        ContextTypeName="AdventureWorksLTDataContext">

    </asp:LinqDataSource>
</body>
</html>
Imports System
Imports System.Collections
Imports System.Configuration
Imports System.Web.DynamicData

PartialPublicClass CustomValidation
    Inherits System.Web.UI.Page
    Protected _table1 As MetaTable, _table2 As MetaTable

    ProtectedSub Page_Init(ByVal sender AsObject, ByVal e As EventArgs)
        ' Register data controls with the data manager.
        DynamicDataManager1.RegisterControl(GridView1)
        DynamicDataManager1.RegisterControl(GridView2)
    EndSubProtectedSub Page_Load(ByVal sender AsObject, ByVal e As EventArgs)
        ' Get the table entities.
        _table1 = GridDataSource.GetTable()
        _table2 = GridDataSource2.GetTable()

        ' Assign title dynamically.
        Title = String.Concat("Customize Validation of the ", _
                              _table1.Name, " and ", _table2.Name, " Tables")

    EndSubEndClass
using System;
using System.Collections;
using System.Configuration;
using System.Web.DynamicData;

publicpartialclass CustomValidation : System.Web.UI.Page
{
    protected MetaTable _table1, _table2;

    protectedvoid Page_Init(object sender, EventArgs e)
    {
        // Register data controls with the data manager.
        DynamicDataManager1.RegisterControl(GridView1);
        DynamicDataManager1.RegisterControl(GridView2);
    }

    protectedvoid Page_Load(object sender, EventArgs e)
    {
        // Get the table entities.
        _table1 = GridDataSource.GetTable();
        _table2 = GridDataSource2.GetTable();

        // Assign title dynamically.
        Title = string.Concat("Customize Validation of the ",
            _table1.Name, " and ",  _table2.Name, " Tables");

    }
}

Compiling the Code

To compile the example code, you need the following:

  • Microsoft Visual Studio 2008 Service Pack 1 or Visual Web Developer 2008 Express Edition Service Pack 1.

  • The AdventureWorksLT sample database. For information about how to download and install the SQL Server sample database, see Microsoft SQL Server Product Samples: Database on the CodePlex site. Make sure that you install the correct version of the sample database for the version of SQL Server that you are running (Microsoft SQL Server 2005 or Microsoft SQL Server 2008).

  • A Dynamic Data Web site. This enables you to create a data context for the database and to create the class that contains the data field to customize and the methods to override. For more information, see Walkthrough: Creating a New Dynamic Data Web Site Using Scaffolding.

See Also

Concepts

ASP.NET Dynamic Data Field Templates Overview

ASP.NET Dynamic Data Model Overview

ASP.NET Dynamic Data Overview

Reference

RequiredAttribute

RequiredAttribute

DynamicValidator

DynamicValidator

Partial Classes and Methods (C# Programming Guide)

Other Resources

LINQ to SQL

The ADO.NET Entity Framework Overview

Change History

Date

History

Reason

July 2008

Added topic.

SP1 feature change.