In the previous recipe, we saw how to decorate our entity classes with NHibernate Validator. A better practice is to extract your validation rules to separate classes and avoid this dependency. In this recipe, I'll show you how to create validator classes, as well as an alternative method for configuring NHibernate Validator.
NHibernate.Validator.dll
from the downloaded ZIP file to your solution's Lib
folder.Eg.Core
model and mappings from Chapter 1.Eg.ClassValidation
.Eg.Core
model and NHibernate.Validator.dll
.ProductValidation
class:public class ProductValidator : ValidationDef<Product> { public ProductValidator() { Define(p => p.Name) .NotNullableAndNotEmpty() .And.MaxLength(255); Define(p => p.Description) .NotNullableAndNotEmpty(); Define(p => p.UnitPrice) .GreaterThanOrEqualTo(0M) .WithMessage("Unit price can't be negative."); } }
Eg.ClassValidation.Runner
.log4net.dll
, NHibernate.dll
, NHibernate.ByteCode.Castle.dll
, and NHibernate.Validator.dll
.App.config
with the standard log4net
and hibernate-configuration
sections, following the Configuring NHibernate with App.config and Configuring NHibernate Logging recipes from Chapter 2.BasicSharedEngineProvider
using the following code:public class BasicSharedEngineProvider : ISharedEngineProvider { private readonly ValidatorEngine ve; public BasicSharedEngineProvider(ValidatorEngine ve) { this.ve = ve; } public ValidatorEngine GetEngine() { return ve; } public void UseMe() { Environment.SharedEngineProvider = this; } }
Program.cs
, use the following code:private static void Main(string[] args) { XmlConfigurator.Configure(); var log = LogManager.GetLogger(typeof (Program)); SetupNHibernateValidator(); var nhibernateConfig = new Configuration().Configure(); nhibernateConfig.Initialize(); ISessionFactory sessionFactory = nhibernateConfig.BuildSessionFactory(); var schemaExport = new SchemaExport(nhibernateConfig); schemaExport.Execute(false, true, false); var junk = new Product { Name = "Spiffy Junk", Description = string.Empty, UnitPrice = -1M }; var ve = Environment.SharedEngineProvider.GetEngine(); var invalidValues = ve.Validate(junk); foreach (var invalidValue in invalidValues) log.InfoFormat("{0} {1}", invalidValue.PropertyName, invalidValue.Message); } private static FluentConfiguration GetNhvConfiguration() { var nhvConfiguration = new FluentConfiguration(); nhvConfiguration .SetDefaultValidatorMode(ValidatorMode.UseExternal) .Register(Assembly.Load("Eg.ClassValidation") .ValidationDefinitions()) .IntegrateWithNHibernate .ApplyingDDLConstraints() .And .RegisteringListeners(); return nhvConfiguration; } private static ValidatorEngine GetValidatorEngine() { var cfg = GetNhvConfiguration(); var validatorEngine = new ValidatorEngine(); validatorEngine.Configure(cfg); return validatorEngine; } private static void SetupNHibernateValidator() { var validatorEngine = GetValidatorEngine(); new BasicSharedEngineProvider(validatorEngine).UseMe(); }
In this recipe, we've separated our validation rules into a separate class named ProductValidation
. Just as we did in our previous recipe, we've decided thateach valid Product
must have a non-null, non-empty Name
and Description
no more than 255 characters long and must have a non-negative UnitPrice
.
As we learned in the previous recipe, we use an ISharedEngineProvider
to locate our validation engine.
Unlike the previous recipe, we use the loquacious, or fluent, syntax to configure NHibernate Validator.
We validate our junk Product
. It fails two validation rules. First, the Description
can't be empty. Second, the UnitPrice
can't be negative. As we see in the log4net output, we get the following validation error messages:
Description may not be null or empty
UnitPrice must be greater than or equal to 0
We can also use NHibernate Validator to validate an entire object graph. Let's take our Movie entity as an example. Suppose we want to ensure that the movie entity is valid, as well as all of its ActorRole children. Our validation class would appear as shown:
Define(m => m.Director) .NotNullableAndNotEmpty() .And.MaxLength(255); Define(m => m.Actors) .HasValidElements();
The HasValidElements
runs the ActorRole validation rules on each object in the Actors
collection.