I've been asked a few times if it's possible to use NHibernate Validator to create a custom validator at the class level, in addition to custom property validators. The short answer is yes. (But it would make a rather uninteresting post if I stopped here.) The documentation at http://nhforge.org/wikis/validator/nhibernate-validator-1-0-0-documentation.aspx gives a hint at how to do it, but doesn't go into a lot of detail. So what follows is an example of creating a class validator for use with NHibernate Validator.
Let's assume you have a class with a requirement that one property be true or another one be true, but not both; i.e., XOR. The end goal would be to use a validator on your class as follows:
[EnforceXorOfProperty1AndProperty2]
public class MyObject
{
public bool MyProperty1 { get; set; }
public bool MyProperty2 { get; set; }
}
To implement this validator, you need to create a validation attribute class along with the validation class to carry out the validation logic. To demonstrate:
[ValidatorClass(
typeof(EnforceXorOfProperty1AndProperty2Validator)),
AttributeUsage(AttributeTargets.Class)]
private class EnforceXorOfProperty1AndProperty2Attribute
: Attribute, IRuleArgs
{
///
/// Since this is a very specialized validator
/// we'll make the message read-only
///
public string Message {
get {
return "Either property 1 or property 2 " +
"needs to be checked, but not both";
}
set { }
}
}
private class EnforceXorOfProperty1AndProperty2Validator
: IInitializableValidator
{
public void Initialize(EnforceXorOfProperty1AndProperty2Attribute attribute) { }
public bool IsValid(object value) {
MyObject myObjectToValidate = value as MyObject;
// DbC to make sure it's being used on a MyObject
Check.Require(myObjectToValidate != null,
"This validator may only be used on a MyObject");
return myObjectToValidate.MyProperty1
^ myObjectToValidate.MyProperty2;
}
}
And that's all there is to it. I typically keep my class-specific validators as sub-classes within the class they decorate to emphasize the tight coupling to the class itself. But you may want to keep them elsewhere if you don't like the idea of validator classes polluting the class they're validating. And if you're making more general purpose validators which can be used by various classes, I typically throw them into MyProject.Core/Validators for keeping them accessible from one place.
For S#arp Architecture users, I frequently always have a need to create validatable DTOs; a useful base class for these DTOs is as follows:
public class ValidatableDto : IValidatable
{
public virtual bool IsValid() {
return Validator.IsValid(this);
}
public virtual ICollection ValidationResults() {
return Validator.ValidationResultsFor(this);
}
private IValidator Validator {
get {
if (validator == null) {
validator = SafeServiceLocator.GetService();
}
return validator;
}
}
[ThreadStatic]
private static IValidator validator;
}
Happy coding!
Billy McCafferty
http://www.itsamuraischool.com
Posted
07-30-2009 3:42 PM
by
Billy McCafferty