Monday, March 25, 2013

Creating Validation Adapter for Validation Attribute

If your project is rather big, often you would like your validation to reside on the business layer instead of on the UI layer. So for example, using our previous code sample, it would make perfect sense if the custom attribute code resides in your business layer project (instead of on the UI project). BUT, to enable client validation (here for example), implementing IClientValidatable is required and IClientValidatable is within the System.Web.Mvc namespace. This means that your business layer needs to reference System.Web.Mvc. But why would you want to do that - referencing a UI related reference in your business layer project?

When we look at the built-in validator (such as RequiredAttribute), how did they do this? If we look at the documentation, it is within System.ComponentModel.DataAnnotations.ValidationAttribute namespace and deriving from System.Object - there is no dependency to System.Web.Mvc at all. We can use that validation attribute in WinForm, Silverlight, WebForm, WPF, etc.


There is no magic - the ASP.NET guys simply built a validation adapter for it in the System.Web.Mvc namespace. In this post, I am going to show you how to build one for our MagicNumber validator.

So let's clarify on our namespacing issue - in our business layer, let's call that MyApp.Library project (namespace: MyApp.Library), which includes MyClass.cs (namespace: MyApp.Library or MyApp.Library.MyClass to be complete) and our custom validation attribute MagicNumberAttribute.cs (namespace: MyApp.Library or MyApp.Library.MagicNumberAttribute to be complete).

Here is the code for MyApp class:
   public class MyClass {
      // ... more code
      [MagicNumber(ErrorMessage = "Sum is not correct")]
      public int MyData { get; set; }
      // ... more code
   }
Here is our code for the custom validation attribute in MagicNumberAttribute.cs:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MagicNumberAttribute : ValidationAttribute {
   // constructor to accept the comparison property name
   public MagicNumberAttribute(string thirtySevenProperty) : base() {
      if (thirtySevenProperty == null) {
         throw new ArgumentNullException("thirtySevenProperty");
      }
      ThirtySevenProperty= thirtySevenProperty;
   }

   // property to store the comparison field name
   public string ThirtySevenProperty{ get; private set; }

   protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
      // get property info of the "ThirtySevenProperty"
      PropertyInfo thirtySevenPropertyInfo = validationContext.ObjectType.GetProperty(ThirtySevenProperty);

      // check if that property exists / valid
      if (thirtySevenPropertyInfo == null) {
         return new ValidationResult(String.Format(CultureInfo.CurrentCulture, "UNKNOWN PROPERTY", ThirtySevenProperty));
      }

      // get the value of the property
      object thirtySevenPropertyValue = thirtySevenPropertyInfo.GetValue(validationContext.ObjectInstance, null);

      // do comparison and return validation result with error if not equal
      if (!Equals(value, thirtySevenPropertyValue)) {
         return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
      }

      // return null if everything is ok
      return null;
   }
}
We'll call our ASP.NET MVC project to be MyApp.UI.Mvc (namespace: MyApp.UI.Mvc). Here is our javascript from the previous post, we'll reuse it, put it in a js file:
   jQuery.validator.unobtrusive.adapters.addSingleVal("magicnumber", "other");

   jQuery.validator.addMethod("magicnumber", function (val, element, other) {
      var modelPrefix = element.name.substr(0, element.name.lastIndexOf(".") + 1)
      var otherVal = $("[name=" + modelPrefix + other + "]").val();
      if (val && otherVal) {
        return val == otherVal;
      }
      return true;
   );
So now, instead of implementing IClientValidatable, we will be creating a validation adapter. We will put this in a file in our MyApp.UI.Mvc project, let's name it "MagicNumberAttributeAdapter.cs". The code is simple:
public class MagicNumberAttributeAdapter : DataAnnotationsModelValidator
{
   public MagicNumberAttributeAdapter(ModelMetadata metadata, ControllerContext context, MagicNumberAttribute attribute)
       : base(metadata, context, attribute)
   {
   }

   public override IEnumerable GetClientValidationRules()
   {
      var rule = new ModelClientValidationRule
      {
          ErrorMessage = this.ErrorMessage,
          ValidationType = "magicnumber",
      };
      rule.ValidationParameters.Add("other", this.Attribute.ThirtySevenProperty);
      yield return rule;
   }
}
To make the connection between the adapter and the validation attribute itself, we need to let MVC know about it - so we need to register it inside the global.asax.cs file:
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MagicNumberAttribute), typeof(MagicNumberAttributeAdapter));
Once all those are setup, our custom validator will fire on the client-side (as well as server-side), we have clear separation of concern - where the business layer does not reference the UI as dependency, and our custom validation attribute is also reusable. Awesome!

Additional reading:
-- read more and comment ...

Sunday, March 17, 2013

Custom Validation Attribute - Client Side Enabled With Custom Rule

In the previous post, I went over on how to enable client-side validation for our custom validation attribute - using a pre-build jQuery validator. Now what if you want to create your own custom script - because your validator needs to do something truly custom that the pre-build validator is not covering? No problem!

Modify your code to this:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MagicNumberAttribute : ValidationAttribute, IClientValidatable {
   // constructor to accept the comparison property name
   public MagicNumberAttribute(string thirtySevenProperty) : base() {
      if (thirtySevenProperty == null) {
         throw new ArgumentNullException("thirtySevenProperty");
      }
      ThirtySevenProperty= thirtySevenProperty;
   }

   // property to store the comparison field name
   public string ThirtySevenProperty{ get; private set; }

   protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
      // get property info of the "ThirtySevenProperty"
      PropertyInfo thirtySevenPropertyInfo = validationContext.ObjectType.GetProperty(ThirtySevenProperty);

      // check if that property exists / valid
      if (thirtySevenPropertyInfo == null) {
         return new ValidationResult(
            String.Format(CultureInfo.CurrentCulture, "UNKNOWN PROPERTY", ThirtySevenProperty));
      }

      // get the value of the property
      object thirtySevenPropertyValue = thirtySevenPropertyInfo.GetValue(validationContext.ObjectInstance, null);

      // do comparison and return validation result with error if not equal
      if (!Equals(value, thirtySevenPropertyValue)) {
         return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
      }

      // return null if everything is ok
      return null;
   }

   public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
      ModelMetadata metadata, ControllerContext context) {
      var rule = new ModelClientValidationRule
      {
          ErrorMessage = this.ErrorMessage,
          ValidationType = "magicnumber",
      };
      rule.ValidationParameters.Add("other", OtherProperty);
      yield return rule;
   }
}
As we see, in "GetClientValidationRules", I am returning a "ModelClientValidationRule" with "ValidationType" set to "magicnumber". This string is the javascript method name. The name itself is not important, but they need to be consistent between what you put here and the actual method name in the javascript method itself. I am also adding parameters into it - which then will be transformed into a name-value pair that can be retrieved in our javascript method.

Now create a javascript file to be included in your project (it must be referenced AFTER the basic jquery.js and jquery.validate.js and jquery.validaten.unobstrusive.js).
   jQuery.validator.unobtrusive.adapters.addSingleVal("magicnumber", "other");

   jQuery.validator.addMethod("magicnumber", function (val, element, other) {
      var modelPrefix = element.name.substr(0, element.name.lastIndexOf(".") + 1)
      var otherVal = $("[name=" + modelPrefix + other + "]").val();
      if (val && otherVal) {
        return val == otherVal;
      }
      return true;
   );
The first line is to connect our javascript method "magicnumber" into the validation event and the rest is our client-side script to do client side validation. Our "magicnumber" method takes in "other" parameter, which will hold the value for the name for our comparison property/field - we need that to get the value of the field. We also want to consider if our model is using prefix - beyond that it is just doing a dom selector and get the value and returning a comparison result.

Additional reading:
-- read more and comment ...

Tuesday, March 12, 2013

Custom Validation Attribute - Client Side Enabled!

In previous posts, I outlined how to make a custom validation attribute - including a more complex one that depends on another property. Both of those validators are working - but they are working on the server-side. So if you have an ASP.NET MVC web application, the validation will fire after it gets to the server side within your controller. Ideally, we want to do client-side validation as well - so that the form does not need to POST if the data is not valid. How do we do that?

We can make custom javascript, query the fields manually and check for conditions - but this code will be disconnected from the custom validator that we created. Doing custom javascript also means that it will be so specific that the likelihood of being reusable is very very small.

Also, as we have seen with the built-in validators that shipped with ASP.NET MVC, they seem to just work - no custom javascript needed per field or manual client-side validation. When a property or a field is decorated with the correct attribute and client-side validation is enabled in the web.config, then boom - it just works. How can we make our validator to behave in similar manner?


One way to do this is by implementing IClientValidatable in your custom validator. Implementing this interface means we must implement a method called "GetClientValidationRules". This method is what basically will become a connector for our validator to the client-side. The property decorated with our custom validation attribute will then outputting flags that will indicate that it should be evaluated during client-side validation. In this method as well we need to provide additional information to properly evaluate the validity of the property on the client-side - mostly a (javascript) method name to be called during validation and some parameters needed to evaluate property within that method. When we do this, this is the updated validator class will look like:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MagicNumberAttribute : ValidationAttribute, IClientValidatable {
   // constructor to accept the comparison property name
   public MagicNumberAttribute(string thirtySevenProperty) : base() {
      if (thirtySevenProperty == null) {
         throw new ArgumentNullException("thirtySevenProperty");
      }
      ThirtySevenProperty= thirtySevenProperty;
   }

   // property to store the comparison field name
   public string ThirtySevenProperty{ get; private set; }

   protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
      // get property info of the "ThirtySevenProperty"
      PropertyInfo thirtySevenPropertyInfo = validationContext.ObjectType.GetProperty(ThirtySevenProperty);

      // check if that property exists / valid
      if (thirtySevenPropertyInfo == null) {
         return new ValidationResult(String.Format(CultureInfo.CurrentCulture, "UNKNOWN PROPERTY", ThirtySevenProperty));
      }

      // get the value of the property
      object thirtySevenPropertyValue = thirtySevenPropertyInfo.GetValue(validationContext.ObjectInstance, null);

      // do comparison and return validation result with error if not equal
      if (!Equals(value, thirtySevenPropertyValue)) {
         return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
      }

      // return null if everything is ok
      return null;
   }

   public IEnumerable<ModelClientValidationRule$gt; GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
      yield return new ModelClientValidationEqualToRule(this.ErrorMessage, OtherProperty);        
   }
}
In the code above, I am reusing an existing validation rule "ModelClientValidationEqualToRule" which will connect with "equalto" jQuery client-validator. This is a pre-existing rule, so at this point, we can just run this and it should work - no more code needed.

In the next post, I will go over on how to return our own custom ModelClientValidationRule and how to create a corresponding javascript client-side validator.

Additional reading:
-- read more and comment ...

Friday, March 8, 2013

Creating Custom Validation Attribute With Dependency To Other Property

In the last post, I went through the steps to make a custom validation attribute - if you missed it, check it out here: Creating Custom Validation Attribute. Now let's say we want to make our validator to look up on the value "37" from another property in our class (instead of hard-coding it). How do we do that? With the help of reflection, we can do that quite easily.

Here is our modified class:
public class MyClass {
   // ... more code
   [MagicNumber("ThirtySeven", ErrorMessage = "Sum is not correct")]
   public int MyData { get; set; }
   // ... more code
   public int ThirtySeven {
      get { return 37; }
   }
   // ... more code
}

In our new custom validator, we override a different IsValid method (the original one is commented out):
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MagicNumberAttribute : ValidationAttribute {
   // constructor to accept the comparison property name
   public MagicNumberAttribute(string thirtySevenProperty) : base() {
      if (thirtySevenProperty == null) {
         throw new ArgumentNullException("thirtySevenProperty");
      }
      ThirtySevenProperty= thirtySevenProperty;
   }

   // property to store the comparison field name
   public string ThirtySevenProperty{ get; private set; }

   // public override bool IsValid(object value) {
   //    var num = int.Parse(value.ToString());
   //    return num == 37;
   // }

   protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
      // get property info of the "ThirtySevenProperty"
      PropertyInfo thirtySevenPropertyInfo = validationContext.ObjectType.GetProperty(ThirtySevenProperty);

      // check if that property exists / valid
      if (thirtySevenPropertyInfo == null) {
         return new ValidationResult(String.Format(CultureInfo.CurrentCulture, "UNKNOWN PROPERTY", ThirtySevenProperty));
      }

      // get the value of the property
      object thirtySevenPropertyValue = thirtySevenPropertyInfo.GetValue(validationContext.ObjectInstance, null);

      // do comparison and return validation result with error if not equal
      if (!Equals(value, thirtySevenPropertyValue)) {
         return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
      }

      // return null if everything is ok
      return null;
   }
}

That's it!
Additional reading:
-- read more and comment ...

Sunday, March 3, 2013

Creating Custom Validation Attribute

In this post, I will outline steps in making custom validation attribute. This is the first post of a series, which eventually will go all the way to making the validation to be able to evaluated in the client side. But, let's not get too far ahead - first we need to make our custom validator.

In this example, I have a simple validator that need to evaluate whether the value of the field "MyData" is equal to the "37".

Our class:
public class MyClass {
   // ... more code
   [MagicNumber(ErrorMessage = "Magic Number is not correct")]
   public int MyData { get; set; }
   // ... more code
}

Our custom validator:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class MagicNumberAttribute : ValidationAttribute {
   public override bool IsValid(object value) {
      var num = int.Parse(value.ToString());
      return num == 37;
   }
}

Done. Simple.
Additional reading:
-- read more and comment ...