The Satisfy (perhaps it sound familiar to some SharpTestsEx users ;) ).
To show how it work the example is a constraint for Person.Name: a Name is significant when it has more than three characters, is composed by letters and the first letter is in uppercase.
To create a that validator, in the classic way, we should create an Attribute implementing IRuleArgs and then a class implementing IValidator, or combine some existing validators for strings (in this case min-length and regex or only a regex).
Using the Satisfier the work to do is so hard as write a lambda:
public class PersonValidation: ValidationDef<Person>The Satisfier is available for those property-types where we consider it has sense.
{
public PersonValidation()
{
Define(p => p.Name)
.Satisfy(name => name != null && name.Length >= 3 && Regex.IsMatch(name,"[A-Z][a-z]*"))
.WithMessage("The Name is not significant.");
}
}
We can extend Loquacious:
public static IChainableConstraint<IStringConstraints> Significant(this IStringConstraints definition)
{
return definition.Satisfy(s => s != null && s.Length >= 3 && Regex.IsMatch(s, "[A-Z][a-z]*"));
}
Now we can use the extension in this way:
public class PersonValidation: ValidationDef<Person>
{
public PersonValidation()
{
Define(p => p.Name).Significant()
.WithMessage("The Name is not significant.");
}
}
public class PetValidation : ValidationDef<Pet>
{
public PetValidation()
{
Define(p => p.Name).Significant()
.WithMessage("The Name is not significant.");
}
}
Note: Don’t take care about the duplication of the massage, that will be matter of another post.
If you want make the Significant constraint a little bit more flexible, you can simply use parameters as:
public static IChainableConstraint<IStringConstraints> Significant(
this IStringConstraints definition,
int minLength)
{
return definition.Satisfy(
s => s != null && s.Length >= minLength && Regex.IsMatch(s, "[A-Z][a-z]*"));
}
and use it as
public class PersonValidation: ValidationDef<Person>
{
public PersonValidation()
{
const int minNameLength = 3;
Define(p => p.Name).Significant(3)
.WithMessage(
string.Format("The Name is not significant (min length {0}).", minNameLength));
}
}
public class PetValidation : ValidationDef<Pet>
{
public PetValidation()
{
const int minNameLength = 4;
Define(p => p.Name).Significant(minNameLength)
.WithMessage(
string.Format("The Name is not significant (min length {0}).",minNameLength));
}
}
As you can see, define a custom-reusable Validator with NHibernate.Validator, is a piece of cake.
Well done!
ReplyDeleteLoquacios is the way, definitely.
Hi Fabio,
ReplyDeleteIs there any reason so IRelationshipConstraints does not derive from ISatisfier as many other constraints interfaces do?
I don't remember exactly but, probably, is because the relation should have its own validation; you shouldn't validate a relation through the aggregate.
ReplyDelete