NHibernate Validator ASP.NET MVC 2 Model Validation
ASP.NET MVC2 will improve the built in Model Validation in a number of ways, including the addition of client side validation (ala xVal). If you would like more information on Model Validation in ASP.NET MVC 2, see Scott Gu’s detailed post on this subject. Out of the box ASP.NET MVC 2 will include support for DataAnnotations, and there are some extensibility points available for plugging in your own framework.
In post series I am going to create a Model Validator using NHibernate Validator (part of NHibernate Contrib). This post will focus on the server-side model validation, and the next post will hopefully focus on adding client-side model validation.
The Setup – Validating Customers
Below is an example of our Customer class which we will use for validation.
public class Customer : BaseObject
{
[NotNullNotEmpty]
[Length(10)]
public virtual string CompanyName { get; set; }
[NotNullNotEmpty]
public virtual string ContactName { get; set; }
[NotNullNotEmpty]
public virtual string Country { get; set; }
[NotNullNotEmpty]
public virtual string Fax { get; set; }
[Range(1, 200, "Must be between 1 and 200")]
public virtual int Age { get; set; }
}
So we are using some NotNullNotEmpty validators, a String Length validator, and a Range validator for testing.
More Setup – Getting the Validator Engine
Eventually we will need to get the validation engine in order to interact with the NHibernate Validator library, and everyone probably has a different way of getting their configured engine. For these examples (and simplicity’s sake) I’m going to use the following engine factory:
public class ValidatorEngineFactory
{
public static ValidatorEngine ValidatorEngine
{
get
{
if (NHibernate.Validator.Cfg.Environment.SharedEngineProvider == null)
{
NHibernate.Validator.Cfg.Environment.SharedEngineProvider = new NHibernateSharedEngineProvider();
}
return NHibernate.Validator.Cfg.Environment.SharedEngineProvider.GetEngine();
}
}
}
Implementing a ModelValidatorProvider
The first step towards hooking up Model Validation is to create a class which inherits from System.Web.Mvc.ModelValidatorProvider. This class has one method you need to override, which is:
IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
Note: GetValidators gets called for all of the properties of your model, so be prepared to have this method called multiple times.
For our implementation, we will grab the validator engine, then attempt to get the ClassValidator for the current Model Type. The Model Type will be different every time GetValidators() is called, so we wait until we have the full class validator (classValidator != null) before returning anything.
/// <summary>
/// Server side validator provider for NHVal
/// </summary>
public class NHibernateValidatorProvider : ModelValidatorProvider
{
/// <summary>
/// Returns model validators for each class that can be validated.
/// When this method is called with a non-class modelType, nothing is added to the yield return
/// (this prevents us from validating the same properties several times)
/// </summary>
public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
{
var validationEngine = ValidatorEngineFactory.ValidatorEngine;
var classValidator = validationEngine.GetClassValidator(metadata.ModelType);
if (classValidator != null)
yield return new NHibernateValidatorModelValidator(metadata, context, classValidator);
}
}
What we are yield returning here (in the case we have a classValidator) is an instance of a new subclass of ModelValidator, which is given the classValidator and will handle the actual validation when called upon by the framework.
Implementing a ModelValidator
Now we need to create a class which inherits from ModelValidator, which will do the actual validation (utilizing the classValidator previously passed in). I’ll show the final code first, and then explain it:
/// <summary>
/// Server side model validator for NHVal
/// </summary>
public class NHibernateValidatorModelValidator : ModelValidator
{
private readonly IClassValidator _validator;
public NHibernateValidatorModelValidator(ModelMetadata metadata, ControllerContext controllerContext, IClassValidator validator)
: base(metadata, controllerContext)
{
_validator = validator;
}
/// <summary>
/// Validate the model associated with this validator
/// </summary>
public override IEnumerable<ModelValidationResult> Validate(object container)
{
var validationResults = _validator.GetInvalidValues(Metadata.Model);
foreach (var validationResult in validationResults)
{
yield return
new ModelValidationResult { MemberName = validationResult.PropertyName, Message = validationResult.Message };
}
}
}
First we create a constructor which takes an IClassValidator, as well as the metadata and controllerContext (which we pass to the base ctor). Here we just save a local instance of the validator for use later. Eventually the framework will call Validate(), which is abstract and must be overridden. There is also a method you can override called GetClientValidationRules(), but since we are concerned only about server-side for now, we’ll leave that for next time.
The Validate method is pretty simple—basically we grab the invalid values for the Model and transform each result into a ModelValidationResult and yield return it.
Hooking up the ModelValidatorProvider
Now we have a ModelValidatorProvider but ASP.NET MVC knows nothing about it, so we go to the global.asax file and register this new provider:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ModelValidatorProviders.Providers.Clear(); //Optional: Remove the default data annotations validations
ModelValidatorProviders.Providers.Add(new NHibernateValidatorProvider()); //Server side validation provider
}
The last line here is the most important – it is where you add the NHibernateValidatorProvider to ASP.NET MVC’s ValidatorProviders collection.
Results – Validation In Action
I made a quick Create action/view according to the built-in templates, which looks a little like the following:
There is more markup, but the point is there is no validation code in here and it is really just using defaults.
Now, let’s try to create the Customer and see what happens:
Just as expected, we fail validation according to the NHibernate Validator Attributes we setup at the beginning of the post. Notice this is all completely integrated with ASP.NET MVC – there is no validation calls in the controller or the view.
The Future, Conan?
I’m really happy how this turned out, but of course server-side validation is only part of the story (though an invaluable part). Part 2 of the series will deal with creating a client-side ModelValidationProvider.
The code above is basically all you need to get server-side validation integrated with ASP.NET MVC 2, though the entire source (including a sample project) can be found at http://github.com/srkirkland/NHValModelValidators.
Enjoy!