Logo

NHibernate

The object-relational mapper for .NET

Diving in NHibernate.Validator

Surfing in the NET, to find some NHibernate.Validator (NHV) example, I saw that there are various things not so clear about how NHV is working. In this post I’ll try to give you a more deep explication.

Class validation definition

In these examples I will use the follow simple class :

public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string CreditCard { get; set; }
}

Using Attributes the definition look like the follow:

public class Customer
{
[Length(Min = 3, Max = 20)]
public string FirstName { get; set; }

[Length(Min=3, Max = 60)]
public string LastName { get; set; }

[CreditCardNumber]
public string CreditCard { get; set; }
}

Using XML mapping the configuration is :

<class name="Customer">
<
property name="FirstName">
<
length min="3" max="20"/>
</
property>
<
property name="LastName">
<
length min="3" max="60"/>
</
property>
<
property name="CreditCard">
<
creditcardnumber/>
</
property>
</
class>


NOTE: In this example I will use the NHV convention for XML Validation-Definition that mean the mapping file is an embedded resource, is in the same folder (namespace) of the class and its name is the name of the class followed by “.nhv.xml” (in this case Customer.nhv.xml). 

Using fluent-interface configuration :

public class CustomerDef: ValidationDef<Customer>
{
public CustomerDef()
{
Define(x => x.FirstName).LengthBetween(3, 20);
Define(x => x.LastName).LengthBetween(3, 60);
Define(x => x.CreditCard).IsCreditCardNumber();
}
}

As you can see you have 3 ways to define validation constraints for a class. For each class, you must use at least one validation definition and at most two; this mean that you can even mix the “Attribute way” with one of the “External” ways (here “external” mean that the validation is defined out-side the class).

The ValidatorEngine

At first, the ValidatorEngine, is your entry-point. If you are using Attributes, you can do something like this:

public void WithOutConfigureTheEngine()
{
var customer = new Customer { FirstName = "F", LastName = "Fermani" };
var ve = new ValidatorEngine();
Assert.That(ve.IsValid(customer), Is.False);
}

What happen behind the scene is:

JITClassMappingFactory:Reflection applied for Customer
ReflectionClassMapping:For class Customer Adding member FirstName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member LastName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member CreditCard to dictionary with attribute CreditCardNumberAttribute

As you can see NHV is investigating the class to know all attributes. Now the same example using two instances of ValidatorEngine

var customer = new Customer { FirstName = "F", LastName = "Fermani" };
var ve1 = new ValidatorEngine();
Assert.That(ve1.IsValid(customer), Is.False);
var ve2 = new ValidatorEngine();
Assert.That(ve2.IsValid(customer), Is.False);

What happen behind is:

JITClassMappingFactory:Reflection applied for Customer
ReflectionClassMapping:For class Customer Adding member FirstName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member LastName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member CreditCard to dictionary with attribute CreditCardNumberAttribute

JITClassMappingFactory:Reflection applied for Customer
ReflectionClassMapping:For class Customer Adding member FirstName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member LastName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member CreditCard to dictionary with attribute CreditCardNumberAttribute

Ups… NHV is investigating the same class two times.

Now again the same example but using only one ValidatorEngine instance:

var customer1 = new Customer { FirstName = "F", LastName = "Fermani" };
var ve = new ValidatorEngine();
Assert.That(ve.IsValid(customer1), Is.False);
var customer2 = new Customer { FirstName = "Fabio", LastName = "Fermani" };
Assert.That(ve.IsValid(customer2), Is.True);

Here we are validating two instances of Customer class using the same ValidatorEngine and what happen behind is:

JITClassMappingFactory:Reflection applied for Customer
ReflectionClassMapping:For class Customer Adding member FirstName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member LastName to dictionary with attribute LengthAttribute
ReflectionClassMapping:For class Customer Adding member CreditCard to dictionary with attribute CreditCardNumberAttribute

As you can see, the class Customer, was investigated only one time, NHV are using reflection only one time.

Conclusion: For performance issue, the ValidatorEngine instance should have the same lifecycle of your application.

The XML convention

As you probably know, I like, very much, all framework complying with rule “DON’T TOUCH MY CODE” (more quite “no invasive framework”). With NHV you can define an “external” XML file as validation definition. The convention, come in place, when you configure the ValidatorEngine to use an “External” source for validation-definitions. The configuration in the application config file is:

    <nhv-configuration xmlns='urn:nhv-configuration-1.0'>
<
property name='default_validator_mode'>UseExternal</property>
</
nhv-configuration>

Given the above first class and embedding the file Customer.nhv.xml in the same namespace, I’m going to run the follow test:

var ve = new ValidatorEngine();
ve.Configure();

var customer1 = new Customer { FirstName = "F", LastName = "Fermani" };
Assert.That(ve.IsValid(customer1), Is.False);
var customer2 = new Customer { FirstName = "Fabio", LastName = "Fermani" };
Assert.That(ve.IsValid(customer2), Is.True);

What happen behind the scene is:

JITClassMappingFactory: - XML convention applied for Customer
XmlClassMapping: - Looking for rules for property : FirstName
XmlClassMapping: - Adding member FirstName to dictionary with attribute LengthAttribute
XmlClassMapping: - Looking for rules for property : LastName
XmlClassMapping: - Adding member LastName to dictionary with attribute LengthAttribute
XmlClassMapping: - Looking for rules for property : CreditCard
XmlClassMapping: - Adding member CreditCard to dictionary with attribute CreditCardNumberAttribute

As you can see, this time, the JITClassMappingFactory (JIT = Just In Time), are using XmlClassMapping instead ReflectionClassMapping. Again the validation definition investigation will be done only one time per ValidatorEngine instance.

If you use XML you can even work without use the convention; in this case you must provide a more complete nhv-configuration section declaring where are mappings files.

The Fluent-Interface

Details about configuration via fluent-interface are available here. To complete the example series, the test is this:

var config = new FluentConfiguration();
config
.SetDefaultValidatorMode(ValidatorMode.UseExternal)
.Register<CustomerDef, Customer>();
var ve = new ValidatorEngine();
ve.Configure(config);

var customer1 = new Customer { FirstName = "F", LastName = "Fermani" };
Assert.That(ve.IsValid(customer1), Is.False);
var customer2 = new Customer { FirstName = "Fabio", LastName = "Fermani" };
Assert.That(ve.IsValid(customer2), Is.True);

What happen behind is:

OpenClassMapping:- For class Customer Adding member FirstName to dictionary with attribute LengthAttribute
OpenClassMapping:- For class Customer Adding member LastName to dictionary with attribute LengthAttribute
OpenClassMapping:- For class Customer Adding member CreditCard to dictionary with attribute CreditCardNumberAttribute
StateFullClassMappingFactory:- Adding external definition for Customer

Here the JITClassMappingFactory don’t is working; NHV is using the StateFullClassMappingFactory during configuration, the JITClassMappingFactory will come in play only after configuration-time. Again the validation definition investigation will be done only one time per ValidatorEngine instance.

The SharedEngineProvider

Perhaps this is the real motivation of this post. As I said above, the ValidatorEngine, should have the same lifecycle of your application, the validation is a cross-cutting-concern and you need to use it from different tiers. In my opinion the better definition of SharedEngineProvider is :

The SharedEngineProvider is the service locator for the ValidatorEngine.

If you are using NHibernate.Validator, especially with its integration with NHibernate, you should define an implementation of ISharedEngineProvider to ensure that, in all your tiers, you are using exactly the same constraints and to avoid more than one ValidatorEngine instances.

The interface is really trivial:

/// <summary>
///
Contract for Shared Engine Provider
/// </summary>
public interface ISharedEngineProvider
{
/// <summary>
///
Provide the shared engine instance.
/// </summary>
/// <returns>
The validator engine.</returns>
ValidatorEngine GetEngine();
}

To configure the SharedEngineProvider you can use the application config or the NHibernate.Validator.Cfg.Environment class before any other task (regarding NHV). Any other configuration will be ignored (in fact you don’t have anything to configure the SharedEngineProvider trough FluentConfiguration).

A good way to implements a SharedEngineProvider is using your preferred IoC container, or using CommonServiceLocator. The SharedEngineProvider is used, where available, by the two listeners for NHibernate integration. A generic configuration look like:

<nhv-configuration xmlns='urn:nhv-configuration-1.0'>
<
shared_engine_provider class='NHibernate.Validator.Event.NHibernateSharedEngineProvider, NHibernate.Validator'/>
</
nhv-configuration>

You should change the class if you want use your own implementation of ISharedEngineProvider. The usage of your own implementation is strongly recommended especially in WEB and even more if you are using an IoC container.

Fabio Maulo.


Posted Thu, 26 February 2009 04:46:00 AM by fabiomaulo
Filed under: validation, HowTo, fluent configuration, Validator

comments powered by Disqus
© NHibernate Community 2024