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:

image image

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:

image

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!

11 Comments

  • I would understand why use the validator engine only to get the classvalidator instead use directly the validator-engine in the ModelValidator

  • studentacad.com

  • Good job. I like this post!

  • Sweet. We currently use CSLA for business rules and I think custom constraints with this would be much more lightweight.

  • I don't mean to snicker; but not knowing anything about NHibernate Validator beforehand I'm not really getting it. What is the benefit in using NHibernate Validator in this way versus plain built-in MVC 2 validation?

    It seems the acceptable input ranges are not reflected from the database schema or NHibernate config. You're putting them in manually, just as one does with the built-in MVC 2 validation...?

    Thanks,

  • @Jasper,
    NHibernate Validator offers a lot of additional functionality against "plain MVC 2 validation" (I'm assuming you are referring to Data Annotations here). For one Data Annotations (DA) doesn't come with its own validation engine, which can be inconvenient esp when testing. NHVal also has many, many more validators than DA (which has like 4) allowing you to do more complex validation. If you need to validate related objects or the size of associated collections this becomes very useful. Also if you are using NHibernate then using NHVal can offer a lot of benefits by hooking up to the NH config and helping generate a better constrained scheema.
    Either way, I think choice is important in software, and I'd like to see Model Validators available for all of the big validation libraries.

  • Easy option to get useful information as well as share good stuff with good ideas and concepts.

  • Any updates on part 2 of this series?

  • Great article, but as I tried implementing my own validation provider, I noticed that the "GetValidators()" method is NEVER called with a ModelType of the root model class - it is only called once for each property of the model class, not for the class type itself.

    Did you see different behavior? I'm using MVC 2 RTW

  • Hello and thanks for this great article, hoping to see more of your interesting articles in the future!

  • To determine if we're dealing with the ModelMetadata for the root object on which we want to perform validation (i.e. not one of the properties) couldn't we just check if metadata.ContainerType == null? I'm not sure what 'validationEngine.GetClassValidator' is doing but perhaps this would be a better performing solution.

Comments have been disabled for this content.