Implementing DataAnnotations

Posted 26 March 2010, 00:29 | by | | Perma-link

Update: As of Entity Framework 4.1, the MinLengthAttribute is no longer needed as it's now part of the framework, but the length is the second parameter in the string format argument list, with the field name in position {0}. 06/06/2011

As I hinted at in my post "Upgrading to ASP.NET MVC 2.0", one of the main remaining changes I wanted to make after upgrading to version 2.0 was to move to using the DataAnnotations that gained first class support.

I've now done this, which meant that I've removed all the code based on the Nerd Dinner validation patterns, and switched to using the Validation Attributes classes.

I started with the basic RequiredAttribute to ensure that all required fields had a value, but then wanted to do a bit more - on my Contact form for example wanted to ensure that the name supplied was at least 3 characters long, that the email address used my rather more rigorous regex than ScottGu's, and that the message was at least 10 characters long.

A simple answer was to use the RegularExpressionAttribute, with settings such as @".{3,}" for the minimum length, and the lengthy pattern for emails, however I wasn't happy with this for a number of reasons:

  1. I wanted to use the "minimum length" validator on multiple properties, with different minimum lengths but very similar error messages including details of the length.
  2. Because my email pattern is compiled using string.Format to make it more manageable, I can't use it in a attribute as it needs to be a "compile time constant".

I also wanted something similar to the IntegerValidator in the Configuration namespace that allows me to specify just a MinValue, rather than the Range Validator, where I needed to supply an arbitrary high value to meet my needs.

As I'm not yet using the .NET 4 framework, I don't have access to the CustomValidatorAttribute, that's no bad thing in my mind, as I'm not a big fan of the "Magic String" form of configuration that I would need to use.

To that end I've created three validators:

public class IsEmailAttribute : RegularExpressionAttribute
public class MinValueAttribute : ValidationAttribute
public class MinLengthAttribute : ValidationAttribute

IsEmailAttribute just supplies the email pattern (built in a read only property) to the base constructor, while MinValueAttribute and MinLengthAttribute both implement the IsValid method, and override the FormatErrorMessage to enable me to include the set value of the validator.

The full code for the MinLengthAttribute is:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class MinLengthAttribute : ValidationAttribute
  public int MinLength { get; set; }

  public MinLengthAttribute(int minLength) {
    MinLength = minLength;

  public override bool IsValid(object value) {
    bool isValid = false;

    var stringValue = value as string;

    if (null != stringValue
        && MinLength <= stringValue.Length) {
      isValid = true;

    return isValid;

  public override string FormatErrorMessage(string name){
    return string.Format(ErrorMessageString, MinLength, name);

The one big problem I've had with doing it this way is that the automation of the client-side validation doesn't work, but as it doesn't plug into the Validation Summary area, this is no great loss, as I'd have to redesign my forms more than I'd like to implement that.

The other major change I had to make to my code was to move to more of a ViewModel pattern - on some of my admin screens I was taking a FormsCollection rather than an explicit model which allowed me to have a photo form with text boxes for Caption, Order and Tags (which holds a comma seperated list of tags), but this doesn't map nicely to the Photo model, where Tags is a collection of Tag models. Writing a ViewModel for editing photos, all the annotations wired up nicely, and gave me much better control of what was being sent to the view.

One thing that was needed uploading photos that I did need to do was handle the case of a user not including an image - thankfully, the previous code set me in good stead:

public ActionResult UploadPhoto(int id,
                                EditPhoto editPhoto,
                HttpPostedFileBase ImageData) {
  ViewData["Title"] = "Create Photo";
  ViewData["Message"] = "Upload a new photo";

  if (ModelState.IsValid
      && null != ImageData
      && ImageData.ContentLength != 0) {
    // Persist to database
    // Return appropriate view based on Request.IsAjaxRequest

  if (null == ImageData || ImageData.ContentLength != 0) {
    // ImageData is missing
    ModelState.AddModelError("ImageData", "You must supply a photo.");

  // Return appropriate view based on Request.IsAjaxRequest

So we can still add additional errors to the ModelState property, allowing us to highlight additional fields that aren't part of the ViewModel.

Where I haven't created a ViewModel, I've used the MetadataTypeAttribute on the partial classes created for the Entity Framework classes:

public partial class Album {
  public string Description {
    get {
      return string.Format(
        "Photos from the album \"{0}\" on{1}/{2}",
        Caption, AlbumId, Caption.CreateSlug());

public class AlbumMetadata {
  [Required(ErrorMessage = "You must supply a caption that is at least 3 characters long.")]
  [MinLength(3, ErrorMessage = "The caption must be at least {0} characters long.")]
  public string Caption { get; set; }

Next time: Implementing FlickrNet.

Filed under: .Net, Entity Framework, Site Updates

Upgrading to ASP.NET MVC 2.0

Posted 17 March 2010, 19:46 | by | | Perma-link

As you might have seen, ASP.NET MVC 2.0 was released this week at MIX along with a handy tool to help upgrade your solutions, so I decided to take the plunge and see how much effort I'd need to go through to upgrade. It turns out not much.

I also managed to move my admin section off to an area, which meant I could finally have a URL structure that allowed me to have duplicate controllers for blogs and albums, which is something I'd wanted since I built them (as opposed to an AdminBlogs controller and an AdminAlbums controller, and it meant that the Tabs controller, used for autocomplete on the photo admin screens, could be nicely grouped with them as well).

The only outstanding piece of work now is to move to using Data Annotations for my form validation, which is where the bulk of the pain I'd encountered lay.

Basically, I'd taken a similar approach as the validation pattern in Nerd Dinner (pp. 37f): I'd created a view model that implemented the following IValidatable interface:

public interface IValidatable
  bool IsValid { get; }
  IEnumerable<RuleViolation> GetRuleViolations();
  void OnValidate();

I initially created an abstract BaseValidater class, but of course you can't have multiple inheritance in C#, so this only worked for my non-entity classes… The RuleViolation is identical to the Nerd Dinner one.

So for each View Model I created an enumerable Errors with yields for each possible error, for example (line breaks added to long strings for formatting):

public IEnumerable<RuleViolation> GetRuleViolations() {
  if (string.IsNullOrEmpty(PostTitle) || 3 > PostTitle.Trim().Length) {
    yield return new RuleViolation("The title is required.", "PostTitle");

  if (3 > PostPath.Trim().Length || !PostPath.IsUrlPart()) {
    yield return new RuleViolation("The path is required, and should only " +
                        "contain alpha-numerics and hyphens.", "PostPath");

  if (50 > PostBody.Trim().Length) {
    yield return new RuleViolation("The post body is required, and should " +
                        "be longer than 50 characters", "PostBody");

And coupled this with the following implementation (tweaked from Nerd Dinner - I prefer using .Any() to .Count() as that only checks to see if there are any values, rather than enumerating over the entire set - not an issue for in memory objects like this, but something to consider with databases Wink):

public bool IsValid {
  get { return (!GetRuleViolations().Any()); }

Then in the controller I went with the following code to handle errors:

if (blog.IsValid)
  return [...]

foreach (RuleViolation violation in photo.GetRuleViolations()) {
  ModelState.AddModelError(violation.PropertyName, violation.ErrorMessage);
             new ValueProviderResult(

The ValueProvider in 1.0 was an IDictionary but this has changed in 2.0 to be an IValueProvider, which means that you can't just iterate through without knowing what the form items were called (which I wasn't, but others are) so my code changed to:

foreach (RuleViolation violation in photo.GetRuleViolations()) {
  ModelState.AddModelError(violation.PropertyName, violation.ErrorMessage);
           new ValueProviderResult(

The only other issue I had was how this was all going to play on IIS 6, but that turned out to be very easy in the RegisterArea method:

public override void RegisterArea(AreaRegistrationContext context) {
        new { action = "Index", id = UrlParameter.Optional }

All in all, very easy and smooth.

Filed under: ASP.NET MVC, Site Updates

In other news

Posted 01 March 2010, 22:02 | by | | Perma-link

A couple of updates on stuff:

  1. Just before Christmas I launched a new website for our church: Holy Trinity Beckenham (let's say it was a "soft launch" Wink). This was built using the amazing N2 CMS, which gave me everything I was looking for in a content management system, running in ASP.NET MVC, without me having to build the bulk of it - I'll do some posts in the future on the tweaks I made to it.
  2. You should now be able to subscribe to the latest blogs and (the new/re-instated) latest photos feeds directly from the homepage of this site. The latest photos can also be found on the albums page, and each album now has it's own latest photos feed as well.

I've also realised that I only mentioned half of the solution to a problem in my last post on moving from Linq to SQL to Entity Framework - namely how I solved the lazy loading of properties - it's there in the picture, but I haven't spelt it out, so there's another post coming on that.

Filed under: Websites