Tuesday, April 28, 2009

NHibernate Automatic Validation

NHibernate is for sure one of the prodigy childs of the .NET open-source movement, yet there are some other great libraries and frameworks. One of them is NHibernate Validator which belongs to NHibernate Contrib project. What it does is to validate your domain entities that supposedly are being persisted using NHibernate, but it provides so much flexibility that you can validate almost every POCO class using attributes.

I’m using it in an ASP.NET MVC project, so first let’s see how to configure and make it work here.

Note: I’m using the trunk build of the Castle Project.

First thing you need to do is to initialize Validator’s engine. Since I’m using it on a web application, let’s initialize and use a shared engine. I’m using the attribute based validation, but you can write the validation logic in your .hbm mapping files.

public class MvcApplication : System.Web.HttpApplication, IMvcApplication
{
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
CreateDependencyInjectionContainer();
RegisterControllerFactory();
RegisterSessionFactory();
RegisterValidatorEngine();
}

private void RegisterValidatorEngine()
{
var config = new NHVConfigurationBase();

config.Properties[Environment.ApplyToDDL] = "false";
config.Properties[Environment.AutoregisterListeners] = "true";
config.Properties[Environment.ValidatorMode] = "UseAttribute";
config.Properties[Environment.SharedEngineClass] = typeof (ValidatorEngine).FullName;
config.Mappings.Add(new MappingConfiguration("MyApp.Domain", null));

Environment.SharedEngineProvider = new NHibernateSharedEngineProvider();
Environment.SharedEngineProvider.GetEngine().Configure(config);

ValidatorInitializer.Initialize(NHibernateConfig);
}
}
and that’s it. You can validate entities passed to your controller’s action by creating a extension method which simplifies things and automatically adds all the errors to ModelState:
public static class ControllerExtensions
{
/// <summary>
///
Validates an entity
/// </summary>
/// <param name="controller"></param>
/// <param name="entity"></param>
public static void Validate(this Controller controller, IValidatable entity)
{
var engine = Environment.SharedEngineProvider.GetEngine();
var errors = engine.Validate(entity);

foreach (var error in errors)
{
controller.ModelState.AddModelError(error.PropertyName, error.Message);
}
}
}
Note that IValidatable is just a marker interface. To call this method and do the actual validation, you need to call the validate method:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Contact(ContactMessage msg)
{
try
{
this.Validate(msg);

if (ModelState.IsValid)
{
this.messengerService.SendMail(msg);
return RedirectToAction("Index");
}
}
catch (Exception ex)
{
ModelState.AddUnhandledError(ex);
}

return View(msg);
}
Easy, right? but it can even get easier! Let’s create an ActionFilter to automagically validate our parameters when the action is invoked:
/// <summary>
///
Automatically validates all the action
/// parameters of type IValidatable.
/// </summary>
public class AutoValidate : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller as Controller;
if(controller == null)
return;

foreach (var entity in GetEntitiesFromParameters(filterContext.ActionParameters))
{
controller.Validate(entity);
}
}

private static IEnumerable<IValidatable> GetEntitiesFromParameters(IEnumerable<KeyValuePair<string, object>> dictionary)
{
var validatableParameters = new List<IValidatable>();

foreach (var keyValue in dictionary)
{
if(keyValue.Value is IValidatable)
{
validatableParameters.Add((IValidatable)keyValue.Value);
}
}

return validatableParameters;
}
}
and your action method looks as simple as this:
[AcceptVerbs(HttpVerbs.Post)]
[AutoValidate]
public ActionResult Contact(ContactMessage msg)
{
if(ModelState.IsValid)
{
try
{
this.messengerService.SendMail(msg);
return RedirectToAction("Index");
}
catch (Exception ex)
{
ModelState.AddUnhandledError(ex);
}
}

return View(msg);
}
Have fun validating!
Submit this story to DotNetKicks Shout it

2 comments:

Gustavo Ringel said...

Hi, NHibernate Validator does not belong to the Castle Project Stack. It is implemented by NHibernate Contributors in the NHContrib SourceForge project.
Please Correct.

Gustavo.

Hadi Eskandari said...

Hi,

My mistake. I kinda wrote this in a hurray. The article is updated though.

Thanks for your comment.