Exploring Dynamic Data: The DataTypeAttribute for Business Logic
Index to this series of articles
Business logic is applied to your Entity classes (objects that describe individual tables where columns are the properties) through metadata. Typically this metadata is defined by applying attributes from the System.ComponentModel.DataAnnotations namespace.
The DataTypeAttribute impacts the “data type” of a column. When defined in the database or a programming language, data types are integers, decimals, strings, etc. We use these to hold all kinds of real world types. Examples:
- Integers: percentage, enumerated type, age
- Decimals: currency, duration, distance, weight
- Strings: phone number, postal code, email address, URL
- DateTime: Date, time, Month/Year (credit card expiration), Birthday (day and month).
Each of these real-world data types have differences in their business rules. The user interface should respect these differences by using different data entry controls (like DateTextBox, Calendar, IntegerTextBox, etc) and validators.
Use the DataTypeAttribute to specify the real world type. Its parameter takes either a value from System.ComponentModel.DataAnnotations.DataType or a string. When the DataType enumerated list lacks the desired type, specify it as a string.
[DataType(DataType.Date)]
public object BirthDate { get; set; }
[DataType("Longitude")]
public object Longitude { get; set; }
Making DataTypeAttribute impact validation
Validation is a very important feature of business rules, and DataTypeAttribute is heavily involved. That’s why DataTypeAttribute is subclassed from System.ComponentModel.DataAnnotations.ValidationAttribute. Yet DataTypeAttribute’s implementation of the IsValid() method is to return true every time.
I recommend subclassing DataTypeAttribute for each data type where validation is needed and implementing the IsValid() method. For example:
public class DateAttribute : DataTypeAttribute
{
public override bool IsValid(object instance)
{
if (instance is DateTime)
return true;
if (instance is string)
{
DateTime vTemp;
return DateTime.TryParse((string)instance, out vTemp)
}
return false;
}
}
public class EmailAddressAttribute : DataTypeAttribute
{
public override bool IsValid(object instance)
{
if (instance is string)
{
return Regex.IsMatch((string) instance,
"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
}
return false;
}
}
How Dynamic Data uses the DataTypeAttribute
ASP.NET Dynamic Data uses the DataTypeAttribute to select a Field Template. Field Templates are User Control files that define the user interface for a specific data type in a specific situation. The situation includes the mode – read-only, edit, or insert – and other ways the user interface may change. Field Templates for edit and insert mode should create Validation web controls based on the business logic.
Here are some guidelines for creating Field Templates which respect DataTypeAttributes:
- The file name of the Field Template should be the same as the value passed into the DataTypeAttribute’s parameter, plus “_Edit” or “_Insert” for the desired mode. For example, DataTypeAttribute(DataType.EmailAddress) should have a Field Template named “EmailAddress_Edit.ascx”.
- The data entry control should be suitable for the data type. Generally you will use a TextBox for string-types, although its not a requirement.
- When using a textbox, always define a Validator web control that will enforce the data type. Typically you will add the CompareValidator, RegularExpressionValidator, or CustomValidator.
- The CompareValidator works for non-string types. Set its Operator property to the desired type. If the Operator doesn’t match the type you need, use one of the other two validators.
- The RegularExpressionValidator works for string types that have a pattern. Your business logic may specify the RegularExpressionAttribute to deliver a regular expression to this validator, letting Dynamic Data automatically connect the two. As a result, there will be two attributes: DataTypeAttribute and RegularExpressionAttributes. It would be nice to combine these two.
- The CustomValidator handles everything else.
The EnumDataTypeAttribute
The EnumDataTypeAttribute is a DataTypeAttribute subclass for one very common case: Enumerated types. It can be assigned to properties whose type is an enumerated type, a string, or integer.
The EnumDataTypeAttribute maps a list of strings to the value stored in the data. It gets those strings from the actual enumerated type definition. For example:
public enum Movement
{
Stop,
Slow,
Normal,
Fast,
Excessive
}
[EnumDataType(typeof(Movement))]
public int VehicleMovement { get; set; }
Dynamic Data provides the Enum.ascx and Enum_Edit.ascx Field Templates to display the list of strings from this attribute. In edit mode, it gives you a DropDownList.
Peter’s soapbox
While its easy to setup, I dislike supplying the list of strings from the enumerated type. Values in the type are usually not correct for the language and lack formatting. In my Peter’s Data Entry Suite, I created an EnumeratedAttribute to handle this. While it can be setup using the enumerated type, you can also define strings explicitly, including culture specific strings.