left-icon

NHibernate Succinctly®
by Ricardo Peres

Previous
Chapter

of
A
A
A

CHAPTER 9

Validation

Validation


NHibernate Validator

A proper data framework allows you to validate entities against business rules and invalid values. NHibernate Validator can be used precisely for that. It is a general purpose validation framework that integrates tightly with NHibernate.

The best way to get it is through NuGet as NHibernate.Validator:

Another way to get it is by downloading it from SourceForge: http://sourceforge.net/projects/nhcontrib/files/NHibernate.Validator.

You can also get the source code from GitHub: https://github.com/darioquintana/NHibernate-Validator.

Once you have set it up, you must decide how you want to apply validation. NHibernate Validator supports configuring validation:

  • By attributes.
  • By XML.
  • By code.

You see, some things in the NHibernate world never change!

To use NHibernate Validator, we need to set up the framework to work together with NHibernate. This is achieved through a listener that performs validation on entities when they are about to be saved or updated. If any validation error occurs, an InvalidStateException is thrown. You can call its GetInvalidValues() to find out exactly what is wrong and fix it.

We integrate NHibernate Validator with the NHibernate Configuration instance before building any session factory. The following shows how to do it with loquacious configuration:

FluentConfiguration validatorConfiguration = new FluentConfiguration();

validatorConfiguration.SetDefaultValidatorMode(ValidatorMode.UseExternal)

.IntegrateWithNHibernate.ApplyingDDLConstraints().RegisteringListeners();

 

NHibernate.Validator.Cfg.Environment.SharedEngineProvider = new NHibernateSharedEngineProvider();

ValidatorEngine validatorEngine = NHibernate.Validator.Cfg.Environment.SharedEngineProvider.GetEngine();

validatorEngine.Configure(validatorConfiguration);

 

cfg.Initialize(validatorEngine);


Tip: Add using statements for the NHibernate.Validator.Cfg.Loquacious, NHibernate.Validator, and NHibernate.Validator.Engine namespaces.

But it is also possible to do it with XML configuration; just make sure you add the following content to your App/Web.config file:

<configuration>

  <configSections>

    <!-- … -->

    <section name="nhv-configuration" type="NHibernate.Validator.Cfg.ConfigurationSectionHandler, NHibernate.Validator" />

  </configSections>

  <nhv-configuration xmlns="urn:nhv-configuration-1.0">

    <property name="apply_to_ddl">true</property>

    <property name="autoregister_listeners">true</property>

    <property name="default_validator_mode">UseExternal</property>

    <mapping assembly="Succinctly.Model"/>

  </nhv-configuration>

  <!-- … -->

</configuration>

And include the following code:

XmlConfiguration xmlConfiguration = new XmlConfiguration();

ValidatorEngine validatorEngine = NHibernate.Validator.Cfg.Environment.SharedEngineProvider.GetEngine();

validatorEngine.Configure(validatorConfiguration);

 

cfg.Initialize(validatorEngine);

If you want, you can also have IntelliSense for the NHibernate Validator XML. If you added NHibernate Validator from NuGet, just copy the nhv-configuration.xsd and nhv-mapping.xsd files from the packages\NHibernate.Validator.1.3.2.4000\lib\Net35 folder to the C:\Program Files (x86)\Microsoft Visual Studio 10.0\Xml\Schemas or C:\Program Files (x86)\Microsoft Visual Studio 11.0\Xml\Schemas, depending on your Visual Studio version. See XML Configuration for more information on this. If you haven’t used NuGet, you will have to extract these files from the distribution .zip file or the source GitHub repository.

Next, we need to configure validations for our entities. First, using code:

FluentConfiguration validatorConfiguration = new FluentConfiguration();

validatorConfiguration.Register(new CustomerValidation()).SetDefaultValidatorMode(ValidatorMode.UseAttribute)

.IntegrateWithNHibernate.ApplyingDDLConstraints().RegisteringListeners();

The CustomerValidation class inherits from NHibernate.Validator.ValidationDef<T> and is defined as follows:

public class CustomerValidation : ValidationDef<Customer>

{

  public CustomerValidation()

  {

    this.ValidateInstance.By((customer, context) => customer.Address != null && /* something else */ )

.WithMessage("The customer address is mandatory");

    this.Define(x => x.Name).NotNullableAndNotEmpty().WithMessage("The customer name is mandatory");

    this.Define(x => x.Name).MaxLength(50).WithMessage("The customer name can only have 50 characters");

    this.Define(x => x.Email).NotNullableAndNotEmpty().WithMessage("The customer email is mandatory");

    this.Define(x => x.Email).MaxLength(50).WithMessage("The customer email can only have 50 characters");

    this.Define(x => x.Email).IsEmail().WithMessage("The customer email must be a valid email adddress");

  }

}

And the same rules, except the ValidateInstanceBy custom validation can be defined in attributes like this:

public class Customer

{

  [NotNullNotEmpty(Message = "The customer name is mandatory")]

  [Length(Max = 50, Message = "The customer name can only have 50 characters")]

  public virtual String Name { get; set; }

 

  [NotNullNotEmpty(Message = "The customer email is mandatory")]

  [Email(Message = "The customer email must be a valid email adddress")]

  [Length(Max = 50, Message = "The customer email can only have 50 characters")]

  public virtual String Email { get; set; }

 

  [NotNull(Message = "The customer address is mandatory")]

  public virtual Address Address { get; set; }

}


Tip: You need to reference the namespace NHibernate.Validator.Constraints.

You have to change the Validator configuration to use attributes:

FluentConfiguration validatorConfiguration = new FluentConfiguration();

validatorConfiguration.SetDefaultValidatorMode(ValidatorMode.UseAttribute)

.IntegrateWithNHibernate.ApplyingDDLConstraints().RegisteringListeners();

Finally, to use XML, you need to add a file named Customer.nhv.xml, place it in the same location as the Customer class, and mark it as an embedded resource, just as we saw on XML Mappings:

<?xml version="1.0" encoding="utf-8" ?>

<nhv-mapping namespace="Succinctly.Model" assembly="Succinctly.Model"

xmlns="urn:nhibernate-validator-1.0">

  <class name="Customer">

    <property name="Name">

      <length max="50" message="The customer name can only have 50 characters"/>

      <notnull-notempty message="The customer name is mandatory"/>

    </property>

    <property name="Email">

      <length max="50" message="The customer email can only have 50 characters"/>

      <notnull-notempty message="The customer email is mandatory"/>

      <email message="The customer email must be a valid email adddress"/>

    </property>

    <property name="Address">

      <not-null message="The customer address is mandatory"/>

    </property>

  </class>

</nhv-mapping>

You also have to tell NHibernate Validator to look for embedded resources explicitly:

FluentConfiguration validatorConfiguration = new FluentConfiguration();

validatorConfiguration.Register(new [] { typeof(Customer) })

.SetDefaultValidatorMode(ValidatorMode.UseExternal)

.IntegrateWithNHibernate.ApplyingDDLConstraints().RegisteringListeners();

For some properties, we are using the built-in validation mechanisms such as NotNullableAndNotEmpty and IsEmail whereas, in other cases, we are performing a custom check (the Address reference). Out-of-the-box validators include:

Included Validators

Validator

Type

Description

Digits/digits/decimalmax/

Decimalmin

Numbers, Strings

Validates the maximum number of digits/digits and fractional digits

FilePathExists/fileexists

Strings

Checks if a file path exists

GreaterThanOrEqualTo/min

Numbers

Checks if a number is greater or equal to a given value

HasValidElements/isvalid

Collections

Checks if all of the collections’ elements are valid recursively

IncludedBetween/range

Numbers

Checks if a number is included within a range (inclusive)

IsCreditCardNumber/

Creditcardnumber

Strings

Checks if a string matches a credit card number

IsEAN/ean

Numbers, Strings

Checks if a string or number matches an EAN

IsEmail/email

Strings

Checks if a string is a valid e-mail address

IsFalse/assertfalse

Booleans

Checks that a boolean is false

IsIBAN/iban

Strings

Checks that a string is a valid IBAN

IsInTheFuture/future

Dates and Times

Checks that a date is in the future

IsInThePast/past

Dates and Times

Checks that a date is in the past

IsIP/ipaddress

Strings

Checks that a string is a valid IP address

IsNumeric/digits

Strings

Checks that a string is a valid number

IsTrue/asserttrue

Booleans

Checks that a boolean is true

IsValid/valid

Entities

Checks that an entity is valid recursively

LengthBetween/length

Strings

Checks that the length of a string is contained within given limits

LessThanOrEqualTo/max

Numbers

Checks that a number is less or equal to a given value

MatchWith/pattern

Strings

Check that a string matches a regular expression

MaxLength/length

Strings

Checks the maximum length of a string

MaxSize/size

Collections

Checks the maximum size of a collection

MinLength/length

Strings

Checks the minimum length of a string

MinSize/size

Collections

Checks the minimum size of a collection

NotEmpty/not-empty

String, Collections, GUIDs

Checks if a string/collection/GUID is not empty

NotNullable/not-null

Any

Checks that a value is not null

NotNullableAndNotEmpty/

notnull-notempty

String, Collections

Checks that a string/collection is not null and contains values

Satisfy

Any

A custom rule, as a lambda expression

SizeBetween/size

Collections

Checks that a collection’s size is contained within given limits

Whitih/range

Numbers

Checks if a number is included within a range (exclusive)

Some of these validations can be implemented on the database in the form of the maximum length for a string and as check constraints. In this case, if NHibernate Validator is integrated with NHibernate using the ApplyingDDLConstraints/apply_to_ddl option, when the model is generated, it will include these checks.

Finally, you can implement your own attributes by inheriting from EmbeddedRuleArgsAttribute and implementing a couple of interfaces:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]

public sealed class IsEvenAttribute : EmbeddedRuleArgsAttributeIRuleArgsIValidator

IPropertyConstraint

{             

  public IsEvenAttribute()

  {

    this.Message = "Number is odd"

  }

       

  public String Message { get; set; }

 

  public Boolean IsValid(Object value, IConstraintValidatorContext constraintValidatorContext)

  {

    Int32 number = Convert.ToInt32(value);

 

    return ((number % 2) == 0);

  }

 

  public void Apply(Property property)

  {

    Column column = property.ColumnIterator.OfType<Column>().First();

    column.CheckConstraint = String.Format("({0} % 2 = 0)", column.Name);

  }

}

This validator checks that a number is even, and can even change the property to which it is applied in order to add this check at the database level. That is what the IPropertyConstraint is for and you don’t have to implement it because, sometimes, a validation cannot be expressed easily in database terms.

To add the attribute by XML, add a <rule> tag to Customer.nhv.xml that references your custom attribute:

<property name="SomeIntegerProperty">

  <rule attribute="IsEvenAttribute">

    <param name="Message" value="Number is odd"/>

  </rule>

</property>

Finally, let me just say this: Validation occurs when NHibernate tries to save an entity with validations attached but you can also validate explicitly:

InvalidValue[] invalidValuesObtainedExplicitly = validatorEngine.Validate(entity);

And that’s just about it. Happy validating!

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.