How to: Customize Data Field Validation in the Data Model Using Custom Attributes
ASP.NET Dynamic Data enables you to customize and extend data field validation at the data layer level. This topic shows how you can add data field validation in the data model by doing the following:
Creating a custom validation attribute. This attribute enables you to create custom metadata that you use in the data model for validation.
Applying the custom validation attribute. After the custom attribute is created, you apply it to the data fields that you want to validate.
See a run-time code example of this feature: Run.
Creating a Custom Validation Attribute
A custom validation attribute lets you create metadata that you can use in the data model to validate data fields. You must derive the custom attribute from the ValidationAttribute base class.
To create a custom validation attribute
In Solution Explorer, right-click the App_Code folder, and then click Add New Item.
Under Add New Item, click Class.
In the Name box, enter the name of the custom validation attribute class. You can use any name that is not already being used. For example, you can enter the name CustomAttribute.cs in Visual C# or CustomAttribute.vb in Visual Basic in order to create a custom attribute class named CustomAttribute.
Add references to the System, System.Globalization, 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; using System.Globalization; using System.ComponentModel.DataAnnotations;
Imports System Imports System.Globalization Imports System.ComponentModel.DataAnnotations
Make the following changes to the class definition:
Make the class not inheritable. Add the NotInheritable keyword in Visual Basic or the sealed keyword in Visual C#.
Derive the class from the ValidationAttribute base type.
Apply the AttributeUsageAttribute attribute to the class definition to state how your custom validation attribute must be used.
The following example shows a class definition. The AttributeUsageAttribute parameters are set so that the custom validation attribute can be applied to properties or fields only one time.
[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
Override the IsValid method and add logic to perform validation. Return true if the custom validation is successful, or false if it fails. The value to be validated is passed to the method as the only parameter.
The following example shows the overridden method.
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
Optionally, override the FormatErrorMessage method to perform custom error-message formatting.
The following example shows how to use the name of the data field that failed the validation to build a custom error message. The ErrorMessageString value is passed as a parameter when the custom attribute is applied to the data field.
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
Save and close the class attribute file.
Applying a Custom Validation Attribute
To customize validation for a data field you must implement a partial class that extends the data model. This enables you to apply the custom attribute to the data field.
To create a partial class for validation
In Solution Explorer, right-click the App_Code folder, and then click Add New Item.
Under Visual Studio installed templates, click Class.
In the Name box, enter the name of the database table you want to add validation for. For example, to add validation for the Customers table, enter "Customer" and name the class Customer.
Add the Partial keyword in Visual Basic or the partial keyword in Visual C# to the class definition to make it a partial class.
If you are creating the class in Visual C#, delete the default constructor.
The following example shows the updated class declaration.
public partial class Customer { }
Partial Public Class Customer End Class
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
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.
public class CustomerMetadata { }
Public Class CustomerMetadata End Class
The associated metadata class provides an object that you can apply validation attributes to.
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
You can now apply the custom validation attribute to a data field.
To apply a custom validation attribute to a data field
In the metadata class, create a property or field whose name corresponds to the data field to validate.
Apply the custom validation attribute that you created earlier to the data field you that you want to validate.
The following example shows how to apply the custom validation attribute to the Phone data field.
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
Save and close the class file.
Example
The following example shows how to create and apply a custom attribute named PhoneMaskAttribute to the Phone data field of the Customer table in the AdventureWorksLT database. The example uses a LINQ-to-SQL class for the data model.
The attribute instructs Dynamic Data to validate the Phone data field against a mask that represents a specific telephone number format. If the telephone number entered by the user does not match the mask, the attribute code issues a custom error.
Imports Microsoft.VisualBasic
Imports System
Imports System.Globalization
Imports System.ComponentModel.DataAnnotations
<AttributeUsage(AttributeTargets.[Property] Or AttributeTargets.Field, AllowMultiple:=False)> _
PublicNotInheritableClass PhoneMaskAttribute
Inherits ValidationAttribute
' Internal field to hold the mask value.ReadOnly _mask AsStringPublicReadOnlyProperty Mask() AsStringGetReturn _mask
EndGetEndPropertyPublicSubNew(ByVal mask AsString)
_mask = mask
EndSubPublicOverridesFunction IsValid( _
ByVal value AsObject) AsBooleanDim phoneNumber AsString = DirectCast(value, String)
Dim result AsBoolean = TrueIfMe.Mask <> NothingThen
result = MatchesMask(Me.Mask, phoneNumber)
EndIfReturn result
EndFunction
' Checks if the entered phone number matches the mask.PublicSharedFunction MatchesMask(ByVal mask AsString, _
ByVal phoneNumber AsString) AsBooleanIf mask.Length <> phoneNumber.Trim().Length Then ' Length mismatch.ReturnFalseEndIfDim i AsInteger = 0
While i < mask.Length
If mask(i) = "d"c _
AndAlsoChar.IsDigit(phoneNumber(i)) = FalseThen ' Digit expected at this position. ReturnFalseEndIfIf mask(i) = "-"c AndAlso phoneNumber(i) <> "-"c Then ' Spacing character expected at this position.ReturnFalseEndIf
System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)
EndWhileReturnTrueEndFunctionPublicOverridesFunction FormatErrorMessage( _
ByVal name AsString) AsStringReturn [String].Format(CultureInfo.CurrentCulture, _
ErrorMessageString, name, Me.Mask)
EndFunctionEndClass
using System;
using System.Globalization;
using System.ComponentModel.DataAnnotations;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealedpublicclass PhoneMaskAttribute : ValidationAttribute
{
// Internal field to hold the mask value.readonlystring _mask;
publicstring Mask
{
get { return _mask; }
}
public PhoneMaskAttribute(string mask)
{
_mask = mask;
}
publicoverridebool IsValid(objectvalue)
{
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.internalbool MatchesMask(string mask, string phoneNumber)
{
if (mask.Length != phoneNumber.Trim().Length)
{
// Length mismatch.returnfalse;
}
for (int i = 0; i < mask.Length; i+)
{
if (mask[i] == 'd' && char.IsDigit(phoneNumber[i]) == false)
{
// Digit expected at this position.returnfalse;
}
if (mask[i] == '-' && phoneNumber[i] != '-')
{
// Spacing character expected at this position.returnfalse;
}
}
returntrue;
}
publicoverridestring 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))> _
PartialPublicClass Customer
EndClassPublicClass CustomerMetadata
<PhoneMask("999-999-9999", _
ErrorMessage:="{0} field value does not match the mask {1}.")> _
Public Phone AsObjectEndClass
using System.Web.DynamicData;
using System.ComponentModel.DataAnnotations;
[MetadataType(typeof(CustomerMetadata))]
publicpartialclass Customer
{
}
publicclass CustomerMetadata
{
[PhoneMask("999-999-9999",
ErrorMessage = "{0} value does not match the mask {1}.")]
publicobject 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
PartialPublicClass CustomAttributeValidation
Inherits System.Web.UI.Page
Protected _table As MetaTable
ProtectedSub Page_Init(ByVal sender AsObject, ByVal e As EventArgs)
' Register control with the data manager.
DynamicDataManager1.RegisterControl(GridView1)
EndSubProtectedSub Page_Load(ByVal sender AsObject, 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")
EndSubEndClass
using System;
using System.Collections;
using System.Configuration;
using System.Web.DynamicData;
publicpartialclass CustomAttributeValidation : System.Web.UI.Page
{
protected MetaTable _table;
protectedvoid Page_Init(object sender, EventArgs e)
{
// Register control with the data manager.
DynamicDataManager1.RegisterControl(GridView1);
}
protectedvoid 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");
}
}
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-driven 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
Reference
Partial Classes and Methods (C# Programming Guide)
Change History
Date |
History |
Reason |
---|---|---|
July 2008 |
Added topic. |
SP1 feature change. |