Property Matching With Data Annotations


Programming in Ruby tutorials and examples

Property Matching With Data Annotations

MaxCDN Content Delivery Network

I found myself needing to compare two properties on a view model when developing a new ASP.NET MVC 2.0 site today. My initial thoughts were I would check the two properties for equality by making a call to my business logic assembly.

I wondered if it would be possible to compare two properties using data annotations. Usually you place the data annotations attribute on a property to validate the data in some way. I quickly knocked up a custom ValdationAttribute that could be placed on a class that checks for equality between two properties of the class. It checks both type and value equality.

using System;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

namespace Model.CustomAnnotations
{
  [AttributeUsage(AttributeTargets.Class)]
    public class MatchAttribute : ValidationAttribute
  {
    public String SourceProperty { get; set; }
    public String MatchProperty { get; set; }

    public MatchAttribute(string source, string match)
    {
      SourceProperty = source;
      MatchProperty = match;
    }

    public override Boolean IsValid(Object value)
    {
      Type objectType = value.GetType();

      PropertyInfo[] properties = objectType.GetProperties();

      object sourceValue = new object();
      object matchValue = new object();

      Type sourceType = null;
      Type matchType = null;

      int counter = 0;

      foreach (PropertyInfo propertyInfo in properties)
      {
        if (propertyInfo.Name == SourceProperty || propertyInfo.Name == MatchProperty)
        {
          if (counter == 0)
          {
            sourceValue = propertyInfo.GetValue(value, null);
            sourceType = propertyInfo.GetValue(value, null).GetType();
          }
          if (counter == 1)
          {
            matchValue = propertyInfo.GetValue(value, null);
            matchType = propertyInfo.GetValue(value, null).GetType();
          }
          counter++;
          if (counter == 2)
          {
            break;
          }
        }
      }

      if (sourceType != null && matchType != null)
      {
        if (Convert.ChangeType(sourceValue, sourceType) == Convert.ChangeType(matchValue, matchType))
          return true;
      }
      return false;
    }
  }
}

Here is how the attribute is placed on a class. The attribute takes two values which are the names of the properties you need to match.

    
[Match("Email", "EmailConfirmation", ErrorMessage = "Emails must match")]
public class PersonViewModel
{
  public int PersonId { get; set; }
  [Required]
    public string Firstname { get; set; }
  [Required]
    public string Lastname { get; set; }
  [Required]
    public string Email { get; set; }
  [Required]
    public string EmailConfirmation { get; set; }
}

This then bubbles up to the UI. If you are using MVC 2.0 this will work out of the box as the default model binder supports data annotations.

Pretty straight forward but very useful! You can download the code here to see it working end to end. You will need MVC 2.0 RC installed as well as Visual Studio Standard Edition or above.