static void

Data Annotations And Dynamic Data

DataAnnotations appeared in 3.5sp1. It is almost the same as the Enterprise Library Validation Application Block. It is part of ASP Dynamic Data (the Linq2Sql/Entity Framework data-driven (CRUD) scaffolding of web-forms websites - with the new DynamicField/ DynamicControl in GridView/DetailsView etc). It can be used generally (and is integrated in asp MVC 2+ and Silverlight 3+).
To use manually, reference System.ComponentModel.DataAnnotations and add the "using". Then add attributes to the (viewModel) class properties.

ValidationAttributes

The following can be applied.

  1. DataType (DateTime, Date, Time, Currency, EmailAddress ...)
  2. Range
  3. RegularExpression
  4. Required
  5. StringLength
  6. + Custom validators inherit from ValidationAttribute

EntityFramework and Linq2Sql generated classes can't be changed, but they are partial, so you can do the (horribly hacky) "buddy class" hack.

[MetadataType(typeof(PersonMetaData))]
public partial class Person
{
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
}
 
public class PersonMetaData
{
    [Required(AllowEmptyStrings=false)]
    [Display(Name = "Last name")]
    [StringLength(30)]
    public string Name { get; set; }
 
    [DataType(DataType.Date)]
    public DateTime DateOfBirth { get; set; }
}

Validate in code

In 3.5 you can't validate in code (unless you reflect the property attributes and call their IsValid- xVal does this in a nice helper for MVC 1).

In 4.0/ Silverlight 3, use the static Validator.TryValidateObject(object, ValidationContext, ICollection<ValidationResult>, true). You need to create a new ValidationContext(object, null, null), the result list and specify you want all the properties validated recursively (the boolean).
Note in .net 4 you can customize the property name with [System.ComponentModel.DataAnnotations.Display(Name="New name")] (do not use the similar [System.ComponentModel.DisplayName("New name")]. Otherwise, set the ErrorMessage= or use resources.

public class Person
{
    public int Id { get; set; }
 
    [Required]
    [Display(Name="Last name")]
    [StringLength(30)]
    public string Name { get; set; }
 
    [DataType(DataType.Date)]
    public DateTime DateOfBirth { get; set; }
}
 
[TestMethod]
public void TestMethod1()
{
    var person = new Person();
    person.Name = new string('c', 40);
 
    var context = new ValidationContext(person, null, null);
    var list = new List<ValidationResult>();
    bool ok = Validator.TryValidateObject(person, context, list, true);
 
    Assert.IsFalse(ok);
    //list contains an result.Message = "The field Last name must be a string with a maximum length of 30."
 
    person.Name = new string('c', 30);
    ok = Validator.TryValidateObject(person, context, list, true);
    Assert.IsTrue(ok);
}

.Net 4 IValidatableObject

System.ComponentModel.DataAnnotations.IValidatableObject is new in .Net 4. It's a more up-to-date version of IDataErrorInfo for server-side class-level validation.

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    if (StartDate > EndDate)
    {
        yield return
            new ValidationResult(
                "StartDate is after EndDate",
                new[] { "StartDate", "EndDate" });
    }
}

This just works with the standard Validator.

//using System.ComponentModel.DataAnnotations;
var context = new ValidationContext(cat, null, null);
var results = new List<ValidationResult>();
if (!Validator.TryValidateObject(cat, context, results))
{
    foreach (var validationResult in results)
    {
        Console.WriteLine(validationResult.ErrorMessage);
    }
}

MVC 2 Server Side

The DefaultModelBinder in MVC 2 reads and uses DataAnnotations. The error message will be written to the Html.ErrorMessageFor

//
// GET: /Person/Edit/5
public ActionResult Edit(int id)
{
    var person = PersonService.Get(id);
    return View(person);
}
 
//
// POST: /Person/Edit/5
[HttpPost]
public ActionResult Edit(int id, Person person)
{
    if (!ModelState.IsValid)
        return View(); //shows errors
 
    PersonService.Save(person);
 
    return RedirectToAction("Index");
}

MVC Templates

In MVC 2, when you use Html.DisplayFor(m => m.MyProperty)/ Html.EditorFor, mvc will check data annotations (so [DataType(DataType.Date)] == "Date", or [UIHint("MyDate")] == "MyDate"). It then looks in DisplayTemplates (or EditorTemplates) under your views, then under shared views, for a ViewUserControl with a corresponding name ("Date.ascx"). If found, the customized format is used.

You can use your model typename, and property templates will be recursively applied.

EditorTemplates in Visual Studio

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue,  new { @class="datePicker" }) %>

This uses the JQueryUI datePicker, specified in the master page.

<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/ui-lightness/jquery-ui.css" type="text/css" rel="Stylesheet" />
<script type="text/javascript" src="http://ajax.googleapis.com//ajax/libs/jquery/1.4.1/jquery.min.js">
</script>
<script type="text/javascript" src="http://ajax.googleapis.com//ajax/libs/jqueryui/1.7.2/jquery-ui.min.js">
</script>
<script type="text/javascript">
$(function() {
 $(".datePicker").datepicker({ dateFormat: 'dd-mm-yy', showOn: 'both', buttonText: '...' });
});
</script>

MVC 2 Client Side Validation

For client side validation in MVC 2, see Haacked. You'll need to reference scripts (there is a jQuery version, but the MvcJquery script is only in the Mvc source code).

<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcValidation.js" type="text/javascript"
></script>
<% Html.EnableClientValidation(); %>
<%= Html.ValidationSummary() %>
<% using (Html.BeginForm())
   {%>
<%=Html.EditorForModel() %>
<br />
<input type="submit" value="Save" />
<% } %>

Broken in MVC rc2: the DataType.Date doesn't validate client-side.

MVC 3 Client Side Validation

In new templates there's AppSettings. For older projects you can add it manually.

<appSettings>
  <add key="ClientValidationEnabled" value="true" />
  <add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>

These don't add the javascript- you must add them manually (jQuery, jquery.validate and MVC's helper jquery.validate.unobtrusive).

<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

For AJAX loaded content, you need to parse the new html: $.validate.unobtrusive.parse("#selector")

Webforms EnableDynamicData

In Asp webforms 4, GridView/DetailsView etc have an .EnableDynamicData(typeof(PersonBuddy)). NB: it's an extension method, so using System.Web.DynamicData. You must use AutoGeneratedColumns or asp:DynamicControl/ asp:DynamicField. Again, the buddy class thing works. You can also customize the UI templates by dropping a DynamicData folder full of templates into your project (copy it from a DynamicData project, which is kinda ugly; they'll automatically override the framework).