Language preference:

Phase 2 / Understanding DataTypeAttributes

Each property has a type, such as string, integer, or boolean, that mimics the type from the Column in the Table (nvarchar, integer).

That type is often not specific enough for developing a user interface or validation rules. For example, a double can be used as a currency, percentage, duration, longitude, etc. DateTime can be used as a Date, Time, both or Duration. The DataTypeAttribute lets you redefine the type and provide more specific validation and formatting rules.

The BLD user interface layer uses DataTypeAttributes to select a Field Template, which describes the user interface for a given type. Instead of just getting a textbox that validates decimals, it may get a CurrencyTextBox or PercentageTextBox with right validators.

Not every DataField (property in the Entity class) needs a DataTypeAttribute. Use the DataTypeAttribute when the DataField must be described more precisely than the database’s datatype.

To apply a DataTypeAttribute, open an Entity class file and go to the property that needs its DataType to be clarified. Then add the attribute to that property. If you are using an Entity Metadata class, (applies to LINQ to SQL and Entity Framework users), the work should be done in the Entity Metadata class. You will have to add a property declaration first, giving it the same name as the Entity's property. However, its type can be 'object' as this is merely a placeholder.

Here is the CategoryMetadata class with two of its properties defined and the appropriate DataTypeAttributes assigned.

<span class='SCKeyword'>public</span> <span class='SCKeyword'>class</span> CategoryMetadata
{
   [DESDA.DataType(DataType.MultilineText)]
   <span class='SCKeyword'>public</span> <span class='SCKeyword'>object</span> Description { <span class='SCKeyword'>get</span>; <span class='SCKeyword'>set</span>; }

   [DESDA.DbImageDataType(BadFormatErrorMessage=<span class='SCString'>&quot;Bad format&quot;</span>, 
      ErrorMessage=<span class='SCString'>&quot;Must be {EXTENSION}&quot;</span>, SupportedFileTypes=<span class='SCString'>&quot;jpg&quot;</span>)]
   <span class='SCKeyword'>public</span> <span class='SCKeyword'>object</span> Picture { <span class='SCKeyword'>get</span>; <span class='SCKeyword'>set</span>; }

}

Here are the DataTypeAttribute classes available in BLD.

Attribute Class Usage
DESDA.ABARoutingNumberAttribute Use when the string contains an ABA Routing Number.
DESDA.AnniversaryDataTypeAttribute For date oriented DataFields containing an anniversary (date without the year).
DESDA.CreditCardNumberAttribute Use when the string contains a credit card number.
DESDA.CurrencyDataTypeAttribute For floating point DataFields that contain a currency value.
DESDA.DataTypeAttribute When none of the other DataTypeAttributes are the right choice. It has numerous options in the System.ComponentModel.DataAnnotations.DataType type.
BLD extends that list with its own enumerated type, PeterBlum.DES.DataAnnotations.ExtendedDataType.
DESDA.DateDataTypeAttribute For date DataFields.
DESDA.DbImageDataTypeAttribute For binary DataFields that contain an image, including gif, jpg, png, tiff, and bmp.
DESDA.DecimalDataTypeAttribute For floating point DataFields. If your DataField is already a floating point type, this is not required, but has extra properties for your business rules.
DESDA.DecimalMeasurementDataTypeAttribute For floating point DataFields whose value reflects a measurement, like gallon, millimeter, and degrees.
DESDA.DurationDataTypeAttribute For time, integer, or floating point DataFields containing a time duration. Integer and floating point values can represent a number of hours, minutes, seconds, and more.
DESDA.EmailAddressDataTypeAttribute For text DataFields containing email addresses.
DESDA.EnumeratedAttribute For integer DataFields that have a fixed list of values and associated text for each value, much like an enumerated type.
DESDA.EnumeratedStringAttribute For text DataFields that have a fixed list of values and associated text for each value, much like an enumerated type.
DESDA.IntegerDataTypeAttribute For integer DataFields. If your DataField is already an integer type, this is not required, but has extra properties for your business rules.
DESDA.IntegerMeasurementDataTypeAttribute For integer DataFields whose value reflects a measurement, like gallon, millimeter, and degrees.
DESDA.MonthYearDataTypeAttribute For date oriented DataFields containing a MonthYear (date without the day of the month).
DESDA.PercentDataTypeAttribute For floating point DataFields that contain a percent value.
DESDA.ListOfValuesDataTypeAttribute A more powerful version of the DESDA.EnumeratedAttribute that allows you to gather data from various resources and display that data in different ways. For example, a Country DataField could get its data from an XML file and display either the full name or abbreviated name.
DESDA.TimeOfDayDataTypeAttribute For date, time, integer, or floating point DataFields containing a time of day (time from 0 to 23:59:59).
DESDA.UrlAsImageDataTypeAttribute For text DataFields containing a URL to an image.
DESDA.UrlDataTypeAttribute For text DataFields containing URLs.

The Source Code Browser shows completed DataAnnotations. The DataTypeAttributes have been highlighted.

In the next topic, you'll learn about ValidationAttributes, the rules that create validation.



Open the Source Code Browser (C# only)
Source Code Browser
 
/* ------------------------------------------------
 * Describes the Entity class for: Category
 * Classes:
 *    Category - Entity class. Edit it for validation and to customize metadata at runtime
 *    CategoryMetadata - Entity Metadata class. It contains the DataAnnotations.
 *    CategoryDAO - BLD DataAccessObject format version of a Data Access Object.

 *    
 * Requires .net 4.0 and these references:
 *    System.ComponentModel.DataAnnotations
 *    PeterBlum.DES
 *    PeterBlum.DES.DataAnnotations
 * Generated: 7/8/2011 4:17:08 PM
 * ------------------------------------------------*/
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using PeterBlum.DES.DAO;
using PeterBlum.DES.DAO.Attributes;
using PeterBlum.DES.DAO.EntityDAO;
using PeterBlum.DES.DataAnnotations;

// Some members of this namespace have identical names to those in System.ComponentModel.DataAnnotations
// making it easy to switch from one to the other by adding the "DESDA." prefix
using DESDA = PeterBlum.DES.DataAnnotations;

namespace PeterBlum.WithDataAnnotations
{
   // --- ENTITY CLASS --------------------------------------------------
   /// <summary>
   /// Entity class. Edit it for validation and to customize metadata at runtime
   /// </summary>
   [EntityDAOType(typeof(CategoryDAO))]
   [MetadataType(typeof(CategoryMetadata))]
   public partial class Category
   {
/// <summary>
/// Associated with the CustomValidationAttribute on the Category.CategoryName property,
/// this uses LINQ to SQL to detect an existing Category with the same name
/// and reports it as an error.
/// </summary>
/// <remarks>
/// <para>You probably will create many of these methods. Consider using stored procedures
/// to search the database for duplicates. Write your code below to invoke the stored procedures.</para>
/// </remarks>
/// <param name="pNewName"></param>
/// <param name="pValidationContext"></param>
/// <returns></returns>
      public ValidationResult CheckForDuplicateCategoryNames(
         string pNewName, ValidationContext pValidationContext)
      {
         BaseDAOChangeEntityActionArgs vArgs = (BaseDAOChangeEntityActionArgs)pValidationContext.GetChangeEntityActionArgs();

         ChangeEntityAction vAction = vArgs != null ? vArgs.Action : ChangeEntityAction.Update;
         if ((vAction == ChangeEntityAction.Insert) || (vAction == ChangeEntityAction.Update))
         {
            Category vCategory = (Category)vArgs.Entity;
            int vThisCategoryID = (vAction == ChangeEntityAction.Insert) ? -1 : vCategory.CategoryID;
            NorthWindDataContext vDataContext = new NorthWindDataContext();
            System.Data.Linq.Table<Category> vTable = vDataContext.Categories;
            if (vTable.FirstOrDefault<Category>(category =>
               (String.Compare(category.CategoryName, pNewName, StringComparison.CurrentCultureIgnoreCase) == 0)
               && (category.CategoryID != vThisCategoryID)) != null)
            {
               DESDA.EntityValidationResult vResult = new DESDA.EntityValidationResult(
                  "CheckForDuplicateCategoryNames", // this is the Source parameter. It can be anything. It is used by the BLDPageManager.UpdateErrorMessage event. So make it useful for detecting this particular error
                  "This name already exists. Choose another.",
                  typeof(Category), "CategoryName", pNewName);
               vResult.SummaryErrorMessage = "{LABEL} already exists. Choose another.";
               return vResult;
            }

         }
         return ValidationResult.Success;
      }
   }  // class Category

   // --- ENTITY METADATA --------------------------------------------------
   /// <summary>
   /// Entity Metadata class.
   /// Companion to the Category Entity class that contains the DataAnnotations
   /// on properties with the same names as those in the actual Entity class.
   /// These properties do not require their types to match those in the Entity class.
   /// An Entity Metadata class allows the Entity class to be regenerated without
   /// overwriting DataAnnotations.
   /// </summary>
   [DESDA.InjectionSecurity(DetectScriptInjection=true, DetectSQLInjection=false)]  // impacts CategoryName, but not Description which has its own rules
   [DESDA.TableRestriction("Admin", DESDA.DenyAccess.None)]
   [DESDA.TableRestriction("Customer", DESDA.DenyAccess.Edit | DESDA.DenyAccess.Delete | DESDA.DenyAccess.Insert)]
   public class CategoryMetadata
   {

      [DESDA.Required()]
      [DESDA.CustomValidation(MethodName="CheckForDuplicateCategoryNames")]
      [DESDA.DisplayName("Name")]
      [DESDA.Filter(AutoGeneratePriority=DESDA.AutoGeneratePriority.Always, InMultiFieldSearch=true)]
      public object CategoryName { get; set; }
   
   /// <summary>
   /// In this example, explicitly make this a Multiline text element
   /// so it benefits from the MultilineText_Edit.ascx Field Template.
   /// Like most large textual fields, it should detect illegal hacking case.
   /// When the Peter's Input Security module is setup, the InjectionSecurityAttribute
   /// will block unwanted input.
   /// </summary>
      [DESDA.DataType(DataType.MultilineText)]
      [DESDA.Filter(InMultiFieldSearch=true)]
      [DESDA.InjectionSecurity(DetectScriptInjection=true, DetectSQLInjection=true, 
         SQLDetectionLevel=PeterBlum.DES.Web.SQLDetectionLevel.MediumLow, 
         HTMLTagMode=PeterBlum.DES.Web.HTMLTagMode.IllegalExceptTags,
         HTMLTags="br|img|span|div|a")]
      public object Description { get; set; }

      [DESDA.DbImageDataType(BadFormatErrorMessage="Bad format", ErrorMessage="Must be {EXTENSION}", SupportedFileTypes="jpg")]
      public object Picture { get; set; }

   }  // class CategoryMetadata

   // --- BLD DATAACCESSOBJECT  --------------------------------------------------
   /// <summary>
   /// BLD DataAccessObject class for the Category Entity class.
   /// It provides CRUD actions. The parent class already has default
   /// methods for Update(), Insert(), Delete() and several queries.
   /// </summary>
   /// <remarks>
   /// <para>For documentation, see the BLD DataAccessObject section of the Business Logic User's Guide.</para>
   /// </remarks>
   [TableName("Categories")]
   public class CategoryDAO : LINQtoSQLEntityDAO<Category>
   {
      public CategoryDAO() : base(typeof(NorthWindDataContext)) { }
      public CategoryDAO(object pDataContext) : base(pDataContext) { }

   }  // class CategoryDAO
}