NHibernate Validator 1.0.0 Documentation
2009-02-15 15:48 Otis's Technology Space 阅读(709) 评论(0) 编辑 收藏 举报本文出处: http://nhforge.org/wikis/validator10/nhibernate-validator-1-0-0-documentation.aspx
Preface
Attributes are a very convenient and elegant way to specify invariant constraints for a domain model. You can, for example, express that a property should never be null, that the account balance should be strictly positive, etc. These domain model constraints are declared in the class itself by annotating its properties. A validator can then read them and check for constraint violations. The validation mechanism can be executed in different layers in your application without having to duplicate any of these rules (presentation layer, data access layer). Following the DRY principle, NHibernate Validator has been designed for that purpose. NHibernate Validator also lets you declare the same rules in XML files. Both approaches (attributes and XML) give us interesting possibilities for configuring our domain rules.
NHibernate Validator works on two levels. First, it is able to check in-memory instances of a class for constraint violations. Second, it can apply the constraints to the NHibernate metamodel and incorporate them into the generated database schema.
Each constraint attribute is associated to a validator implementation responsible for checking the constraint on the entity instance. A validator can also (optionally) apply the constraint to the NHibernate metamodel, allowing NHibernate to generate DDL that expresses the constraint. With the appropriate event listener, you can execute the checking operation on inserts and updates done by NHibernate. NHibernate Validator is not limited to only be used with NHibernate. You can easily use it anywhere in your application as well, indeed, you can use it at UI validation with Winforms or Asp.Net technologies.
When checking instances at runtime, NHibernate Validator returns information about constraint violations in an array of InvalidValues. Among other information, the InvalidValue contains an error description message that can embed the parameter values bundle with the annotation (eg. length limit), and message strings that may be externalized to a ResourceManager.
About the project
NHibernate Validator is a powerful and extensible framework to validate objects using the .Net Platform. Born as a port of Hibernate Validator 3.0.0 project at december of 2007. Later on, many improvements and changes were made to the Core and API, and now we can say that NHibernate Validator is more than just a port. NHibernate Validator is entirely written in C#. The first release of NHibernate Validator was entirely developed by Dario Quintana, Gustavo Ringel and Fabio Maulo. We appreciate the help of Davy Brion in the revision of this document.
NHibernate Validator is part of the NHibernate Contrib project and you can download it here.
You can ask questions about the development and utilization of NHibernate Validator on the following mailing lists:
The development mailing list can be found here
The user forum can be found here
Bug and issues report should be sent to the development mailing list.
Using the Validator framework
NHibernate Validator is intended to be used to implement multi-layered data validation, where constraints are expressed in a single place and checked in various different layers of the application.
This chapter will cover NHibernate Validator usage for different layers
Database schema-level validation
Out of the box, NHibernate Validator will translate the constraints you have defined for your entities into mapping metadata. For example, if a property of your entity has the attribute [NotNull], its columns will be declared as not null in the DDL schema generated by NHibernate.
Using hbm2ddl, domain model constraints will be expressed into the database schema.
If, for some reason, the feature needs to be disabled, set apply_to_ddl to false.
ORM integration
NHibernate Validator integrates with NHibernate in a native way, and knows how to treat every persistent entity.
NHibernate event-based validation
NHibernate Validator has two built-in NHibernate event listeners. Whenever a PreInsertEvent or PreUpdateEvent occurs, the listeners will verify all constraints of the entity instance and throw an exception if any of them are violated. Basically, objects will be checked before any insert and before any update triggered by NHibernate. This includes cascading changes! This is the most convenient and easiest way to activate the validation process. If a constraint is violated, the event will raise a runtime InvalidStateException which contains an array of InvalidValues describing each failure.
If, for some reason, you want to disable this integration, set autoregister_listeners to false
Note: If the entities are not annotated with validation attributes, there is no runtime performance cost.
In case you wish to set manually the event listeners for NHibernate Core, use the following configuration inhibernate.cfg.xml:
<hibernate-configuration>
...
<event type="pre-update">
<listener class="NHibernate.Validator.Event.ValidatePreUpdateEventListener, NHibernate.Validator"/>
</event>
<event type="pre-insert">
<listener class="NHibernate.Validator.Event.ValidatePreInsertEventListener, NHibernate.Validator"/>
</event>
</hibernate-configuration>
Application-level validation
In order to interact with NHibernate Validator in your application you must use the main class: ValidatorEngine.
ValidatorEngine validator = new ValidatorEngine();
bool isValid = validator.IsValid(customer);
These two lines are enough to validate an object with its validation rules, which can be configured using Attributes or XML. The first line is the instantiation of the validator engine of NHibernate Validator. The second line is the validation of an object, and the result of the IsValid(...)method indicates if the object is valid or not. In order to get the invalid values of an object, you can do:
ValidatorEngine validator = new ValidatorEngine();
InvalidValue[] validationMessages = validator.GetInvalidValues(customer);
Now instead of only knowing if the object is valid or not, we get an array of InvalidValues. If this array length is greater than zero then there are validation errors and you can traverse the InvalidValue array to get verbose information about the problems that cause the object to be invalid.
Presentation layer validation
[TODO]
Validation informations
NHibernate Validator provides an array of InvalidValue. Each InvalidValue has several methods describing the reasons that caused the entity to be invalid.
BeanClass retrieves the failing bean type
Bean retrieves the failing instance (if any ie not when using GetPotentialInvalidValues())
Value retrieves the failing value
Message retrieves the proper internationalized error message
RootBean retrieves the root bean instance generating the issue (useful in conjunction with [Valid]), is null ifGetPotentialInvalidValues(...) is used.
PropertyPath retrieves the dotted path of the failing property starting from the root bean
Defining constraints
What is a constraint?
A constraint is a rule that a given element (field, property or type) has to comply to. The rule semantic is expressed by an Attribute or configured in Xml files. A constraint may have some properties to configure the associated validator, for example a Min constraint will have a value property setting the minimum acceptable value for the element to be valid.
Built in constraints
Hibernate Validator comes with some built-in constraints, which cover most of the basic data checks. As we'll see later, you're not limited to them, you can literally in a minute write your own constraints.
Table Built-in constraints
<tbody> </tbody> <tbody> <tbody> </tbody> </tbody>
Constraint
Apply on
Runtime checking
NHibernate Metadata impact
Length(Min=, Max=)
property (String)
checks if the string length is between the min-max range
Column length will be set to max
Max(Value=)
property (numeric or string representation of a numeric)
checks if the value is less than or equals to max
Add a check constraint on the column
Min(Value=)
property (numeric or string representation of a numeric)
checks if the value is more than or equals to min
Add a check constraint on the column
NotNull
property
checks if the value is not null
Column(s) are not null
NotEmpty
property
checks if the string is not empty.
NotEmptyOrNull
property
Checks if the string is not null or not empty.
Past
property (DateTime)
check if the date is in the past
Future
property (DateTime)
check if the date is in the future
Pattern(Regex="regexp", Flag=)
property (string)
check if the property matches the regular expression given a match flag. The<tt>Flag</tt> parameter is a System.Text.RegularExpressions.RegexOptions enum. You can also declare multiple Pattern attributes on the same member.
Range(Min=, Max=)
property (numeric or string representation of a numeric)
check if the value is between Min and Max (included)
Size(Min=, Max=)
property (array, collection, map)
check if the element size is between Min and Max (included)
AssertFalse
property
check that the method evaluates to false (useful for constraints expressed in code rather than attributes)
AssertTrue
property
check that the method evaluates to true (useful for constraints expressed in code rather than attributes)
Valid
property (object)
perform validation recursively on the associated object. If the object is a Collection or an array, the elements are validated recursively. If the object is a Map, the value elements are validated recursively.
Email
property (String)
check whether the string conforms to the email address specification
Digits
property (numeric or string representation of a numeric)
checks whether the property is a number having up to integerDigits integer digits andfractionalDigits fractonal digits
define column precision and scale
Error messages
NHibernate Validator comes with a default set of error messages translated in several languages (if yours is not present, please send us a patch). You can add your own additional set of messages while writing your validator Attributes or at XML. If NHibernate Validator cannot resolve a key from your ResourceManager nor from ValidatorMessage, it falls back to the default built-in values.
Alternatively you can provide a ResourceManager while checking programmatically the validation rules on a bean or if you want a completly different interpolation mechanism, you can provide an implementation of NHibernate.Validator.Engine.IMessageInterpolator.
Writing your own constraints
Extending the set of built-in constraints is extremely easy. Any constraint consists of two pieces: the constraintdescriptor (the attribute) and the constraint validator (the implementation class). Here is a simple user-defined descriptor:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
[ValidatorClass(typeof(PhoneValidator))]
public class PhoneAttribute : Attribute, IRuleArgs
{
private string message = string.Empty;
public string Message
{
get { return message; }
set { message = value; }
}
}
To link a descriptor to its validator implementation, we use the [ValidatorClass] meta-attribute. Here you can see an implementation of a constraint validator:
public class PhoneValidator : IValidator
{
public bool IsValid(object value)
{
Regex regex = new Regex(@"^[2-9]\d{2}-\d{3}-\d{4}$");
if (value == null) return true;
return regex.IsMatch(value.ToString());
}
}
This PhoneValidator class is simple. The method IsValid() receives a string that should be validated with a Regex expression. The IsValid() method should return false if the constraint has been violated.
If you need to initialize the class validator you can implement the interface IInitializableValidator<A> and the class should look like this:
public class PhoneValidator : IInitializableValidator<PhoneAttribute>
{
private Regex regex;
public bool IsValid(object value)
{
if (value == null) return true;
return regex.IsMatch(value.ToString());
}
public void Initialize(PhoneAttribute parameters)
{
regex = new Regex(@"^[2-9]\d{2}-\d{3}-\d{4}$");
}
}
As you can see, implementing the IInitializableValidator<A> interface we have to implement the methodInitialize(...) that initializes the validator, and it's executed first of all.
The IsValid() method should return false if the constraint has been violated. For more examples, refer to the built-in validator implementations.
We now have to implement the validator (ie. the rule checking implementation). A validation implementation can check the value of the a property (by implementing PropertyConstraint ) and/or can modify the NHibernate mapping metadata to express the constraint at the database level (by implementing PersistentClassConstraint)
We have only seen property level validation, but you can write a class level validation attribute. Instead of receiving the return instance of a property, the bean itself will be passed to the validator. To activate the validation checking, just put the validation attribute to the class itself instead of in a property. A small sample can be found in the unit test suite.
If your constraint can be applied multiple times (with different parameters) on the same property or type, you can use set the AllowMultiple parameter to true on the AttributeUsage attribute. An example of this it's the built-in validator Pattern, have a look:
[Serializable]
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]
[ValidatorClass(typeof (PatternValidator))]
public class PatternAttribute : Attribute, IRuleArgs
{
…
}
Configuring your domain model with .Net Attributes
Since you are already familiar with attributes now, the syntax should be very familiar
public class Address
{
// a static non null field
[NotNull]
public static string blacklistedZipCode;
// field a not null string of 20 characters maximum
[Length(Max = 20), NotNull]
private string country;
private long id;
private bool internalValid = true;
private string line1;
private string line2;
private string state;
private string zip;
// a numeric between 1 and 2000
[Min(1), Range(Max = 2000)]
public long Id
{
get { return id; }
set { id = value; }
}
// respond to the constraint in the field: country
public string Country
{
get { return country; }
set { country = value; }
}
// a non null string
[NotNull]
public string Line1
{
get { return line1; }
set { line1 = value; }
}
// a not null string of 3 characters maximum
[Length(Max = 3), NotNull]
public string State
{
get { return state; }
set { state = value; }
}
// a not null numeric string of 5 characters maximum
// if the string is longer, the message will
// be searched in the resource bundle at key 'long'
[Length(Max = 5, Message = "{long}")]
[Pattern(Regex = "[0-9]+")]
[NotNull]
public string Zip
{
get { return zip; }
set { zip = value; }
}
// no constraint
public string Line2
{
get { return line2; }
set { line2 = value; }
}
// should always be true
[AssertTrue]
public bool InternalValid
{
get { return internalValid; }
set { internalValid = value; }
}
}
While the example only shows public property validation, you can also annotate fields of any kind of visibility. In the example MyBeanConstraint is a custom Class Validator, we will talk about this later.
[MyBeanConstraint(Max=45)]
public class Dog
{
[AssertTrue] private bool IsMale;
[NotNull] protected string Name { get {...} set{...} }
…
}
You can also annotate interfaces. NHibernate Validator will check all superclasses and interfaces extended or implemented by a given type to read the appropriate validator attributes.
public interface Named
{
[NotNull] string Name {get; set;}
...
}
public class Dog : Named
{
[AssertTrue] private bool isMale;
public string Name { get {...} set{...} }
}
The name property will be checked for nullity when the Dog type is validated.
Configuring your domain model with XML
You also can configurate your domain model with constraints using xml files: the nhv.xml files. We prefer to embed these Xml files into the assemblies, and NHibernate Validator is prepared to inspect them and retrieve the resources with constrains, like NHibernate does with hbm.xml files.
<?xml version="1.0" encoding="utf-8" ?>
<nhv-mapping xmlns="urn:nhibernate-validator-1.0"
namespace="NHibernate.Validator.Tests.Base"
assembly="NHibernate.Validator.Tests">
<class name="Address">
<property name="blacklistedZipCode">
<not-null/>
</property>
<property name="country">
<length max="20"/>
<not-null />
</property>
<property name="floor">
<range min="-2" max="50" message="{floor.out.of.range} (escaping #{el})" />
</property>
<property name="Id">
<min value="1" />
<range max="2000" />
<max value="2500"/>
</property>
<property name="Line1" >
<not-null/>
</property>
<property name="State">
<not-null/>
<length max="3"/>
</property>
<property name="Zip">
<length max="5" message="{long}"/>
<pattern regex="[0-9]+" />
<not-null/>
</property>
<property name="InternalValid">
<asserttrue />
</property>
</class>
</nhv-mapping>
We strongly recommend to validate every nhv.xml file of your application with the respective XSD file. Everynhv.xml file should be validated with the nhv-mapping.xsd file included within the NHibernate Validator distribution whether in source or binaries. Indeed, if you are using an IDE like Visual Studio it offers IntelliSense support in the nhv.xml file if you add the schema to Visual Studio's xml schemas folder.
Xml beauties: The nicest part of Xml for validation is that you can put xml validators in a different dll so you don't need to recompile your domain when validation rules change. Only the dll with the Xml files..
Configuration
NHibernate Validator has global parameters that you can configure at the startup. This configuration can be done in differents ways: programmaticaly or using xml files. If you are involved with NHibernate this could be very familiar to you.
Configuration properties:
Table Configuration properties
<tbody> </tbody> <tbody> <tbody> </tbody> </tbody>
Property name
Purpose
apply_to_ddl
Configure NHibernate to apply the constraints to the DDL.
autoregister_listeners
Register the Event-Listeners automatically on Inserts and Updates.
default_validator_mode
Define what are the constraints sources are and how to proceed. There are some built-in configurations:
-
UseAttribute (default): Use Attributes to configurate the validators on types and/or members. Xml is ignored.
-
UseXml: Use Xml to configurate the validators on types and/or members. Attributes are ignored.
-
OverrideAttributeWithXml: If the same type/member is annotated with an Attribute and has a Xml rule the Attribute annotation will be used.
-
OverrideXmlWithAttribute: If the same type/member is annotated with an Attribute and has a Xml rule the Attribute annotation will be used.
assembly
Which are the assemblies with contains the nhv.xml files embedded. You can have more than one assembly.
shared_engine_provider
To inject a custom Shared Engine Provider.
Programmatic configuration
You can use the following code to configure NHibernate.Validator programmatically:
using NHibernate.Validator.Cfg;
using NHibernate.Validator.Engine;
using using Environment=NHibernate.Validator.Cfg.Environment;
…
ValidatorEngine ve = new ValidatorEngine();
NHVConfiguration nhvc = new NHVConfiguration();
nhvc.Properties[Environment.ApplyToDDL] = "false";
nhvc.Properties[Environment.AutoregisterListeners] = "false";
nhvc.Properties[Environment.ValidatorMode] = GetMode();
nhvc.Mappings.Add(new MappingConfiguration("NHibernate.Validator.Demo.Model",null));
ve.Configure(nhvc);
The ValidatorEngine will be set with the configuration. The NHibernate Validator configuration is represented by theNHibernate.Validator.Cfg.NHVConfiguration type. Other class used to configure all the stuff is theNHibernate.Validator.Cfg.Environment type, this class contain the string with the names of the properties of the configuration of NHibernate Validator.
Declarative configuration: using Xml files
The declarative configuration keeps configuration out of the code. This means configuring the application using a different file, in this case a XML file. But the options don't end here: you can choose to configure NHibernate Validator using a simple Xml file or you can put the configuration in the well known App.Config/Web.Config.
Simple xml file
This is a sample of a simple configuration in a xml file:
<?xml version="1.0" encoding="utf-8" ?>
<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'>UseXml</property>
<mapping assembly='NHibernate.Validator.Demo.Model'/>
</nhv-configuration>
We encourage to name the configuration file nhvalidator.cfg.xml, because NHibernate Validator will pick up this file from the application directory in order to get the configuration parameters, however you can name this file as you wish.
In order to configurate NHibernate Validator with the nhvalidator.cfg.xml you can use the following codeו:
ValidatorEngine engine = new ValidatorEngine();
engine.Configure(); // by convention reads the nhvalidator.cfg.xml file.
You can choose another name for your configuration file and proceed of this manner:
ValidatorEngine engine = new ValidatorEngine();
engine.Configure("path/to/MyNHVConfigurationFile.xml"); // you can use whatever path to point to the configuration file
Configuration Application file: app.config/web.config
The configuration can be declared into our configuration application .Net files: app.config or web.config. In the below example you will see how to do it.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="nhv-configuration"
type="NHibernate.Validator.Cfg.ConfigurationSectionHandler, NHibernate.Validator" />
...
</configSections>
...
<nhv-configuration xmlns='urn:nhv-configuration-1.0'>
<shared_engine_provider
class='NHibernate.Validator.Event.NHibernateSharedEngineProvider, NHibernate.Validator'/>
</nhv-configuration>
...
</configuration>