Tuesday, April 14, 2009

Spring.NET Short Circuiting Validators - The Finally

I really like what I do, I love the internet and love hearing other's ideas. After posting part 2 of this topic I noticed someone had actually commented on one of my very few posts. Mark Pollack suggested a great interim solution to the problem of easily configuring short circuiting validators. I say interim because the ability to do something like <v:group fastValidate=true> is planned fro Spring 2.0. Mark's solution allows me to preserve the initial rules layout that was presented in part 1. Here it is a again

    1 <objects xmlns="http://www.springframework.net"

    2         xmlns:v="http://www.springframework.net/validation">

    3   <v:group id="UserValidator">

    4     <v:group id="UserNameValidator">

    5       <v:required id="UserNameRequired" test="UserName">

    6         <v:message id="Errors.UserName.Required"  providers="UserNameError"/>

    7       </v:required>

    8 

    9       <v:regex id="UserNameMinmumComplexity" test="UserName">

   10         <v:property name="Expression" value="^.{5,20}$"/>

   11         <v:message id="Errors.UserName.MinimumComplexity" providers="UserNameError"/>

   12       </v:regex>

   13 

   14       <v:validator id="UserNameIsUnique" test="UserName" type="XXX.Infrastructure.Validation.IsUniqueUserNameValidator, XXX.Infrastructure">

   15         <v:property name="UserRepository" ref="UserRepository"/>

   16         <v:message id="Errors.UserName.IsUnique" providers="UserNameError"/>

   17       </v:validator>

   18     </v:group>

   19 

   20   ....

   21   </v:group

   22 

   23  ....

   24 </objects>



His suggestion is to use IObjectPostProcessor to set the FastValidate property. Since I don't want to set the property on every ValidatorGroup I adopted a naming convention for those groups I do desire the behavior and so I can easily identify them in the post processor. For example, In the configuration above I changed UserNameValidator to UserNameFastValidator. The post processor looks like,

    1     public class FastValidatePostProcessor

    2         : IObjectFactoryPostProcessor

    3     {

    4         public void PostProcessObjectFactory(IConfigurableListableObjectFactory factory)

    5         {

    6             string[] validatorGroupNames = factory.GetObjectNamesForType(typeof(ValidatorGroup));

    7             foreach (string validatorGroupName in validatorGroupNames)

    8             {

    9 

   10                 if (validatorGroupName.EndsWith("FastValidate"))

   11                 {

   12                     IObjectDefinition definition = factory.GetObjectDefinition(validatorGroupName);

   13                     definition.PropertyValues.Add("FastValidate", true);

   14                 }

   15             }

   16         }

   17     }



All that is left is to add the object to the configuration file something like

    1 <object type="XXX.Infrastructure.Validation.FastValidatePostProcessor, XXX.Infrastructure"/>



Thanks Mark!

Spring.NET Short Circuiting Validators - Part 2

Yesterday I gave a possible solution to the problem of easily configurable short circuiting validators in Spring.NET. Several hours later another possiblity struck me. One can use the existing ValidatorGroup class with the <v:validator> tag and set FastValidate in the <v:property> tag instead using the <v:group> tag which doesn't let you set that property. The configuration would look like this


    1 <objects xmlns="http://www.springframework.net"

    2         xmlns:v="http://www.springframework.net/validation">

    3   <v:group id="UserValidator">

    4     <v:validator id="UserNameValidator" type="Spring.Validation.ValidatorGroup, Spring.Core">

    5       <v:property name="FastValidate" value="true"/>

    6       <v:property name="Validators">

    7         <list>

    8           <ref object="UserNameRequired"/>

    9           <ref object="UserNameMinimumComplexity"/>

   10           <ref object="UserNameIsUnique"/>

   11         </list>

   12       </v:property>

   13     </v:validator>

   14 

   15     <!-- ...more validators -->

   16   </v:group>

   17 

   18   <v:required id="UserNameRequired" test="UserName">

   19     <v:message id="Errors.UserName.Required"  providers="UserNameError"/>

   20   </v:required>

   21 

   22   <v:regex id="UserNameMinimumComplexity" test="UserName">

   23     <v:property name="Expression" value="^.{5,20}$"/>

   24     <v:message id="Errors.UserName.MinimumComplexity" providers="UserNameError"/>

   25   </v:regex>

   26 

   27   <v:validator id="UserNameIsUnique" test="UserName" type="XXX.Infrastructure.Validation.IsUniqueUserNameValidator, HSN.Infrastructure">

   28     <v:property name="UserRepository" ref="UserRepository"/>

   29     <v:message id="Errors.UserName.IsUnique" providers="UserNameError"/>

   30   </v:validator>

   31 

   32   <!-- ...more validators -->

   33 </objects>

Monday, April 13, 2009

Spring.NET Short Circuiting Validators - Part 1

One of the features I've come to like in Spring.NET is the validation library, but it does come up short on at least one basic need. That is a straight forward way to configure short circuiting for <v:group>'s. Meaning a validator group that quits validating and returns false on the first failed validator and returns true otherwise. Out of the box if you wanted to setup a group to, let's say, validate the User.UserName property you might write something like,

    1 <objects xmlns="http://www.springframework.net"

    2         xmlns:v="http://www.springframework.net/validation">

    3   <v:group id="UserValidator">

    4     <v:group id="UserNameValidator">

    5       <v:required id="UserNameRequired" test="UserName">

    6         <v:message id="Errors.UserName.Required"  providers="UserNameError"/>

    7       </v:required>

    8 

    9       <v:regex id="UserNameMinmumComplexity" test="UserName">

   10         <v:property name="Expression" value="^.{5,20}$"/>

   11         <v:message id="Errors.UserName.MinimumComplexity" providers="UserNameError"/>

   12       </v:regex>

   13 

   14       <v:validator id="UserNameIsUnique" test="UserName" type="XXX.Infrastructure.Validation.IsUniqueUserNameValidator, XXX.Infrastructure">

   15         <v:property name="UserRepository" ref="UserRepository"/>

   16         <v:message id="Errors.UserName.IsUnique" providers="UserNameError"/>

   17       </v:validator>

   18     </v:group>

   19 

   20   ....

   21   </v:group

   22 

   23  ....

   24 </objects>



However when User.UserName is validated every validator will fire. There are many reasons why it makes sense to abandon validation on the first failed validator: some rules maybe more expensive than others to evaluate, maybe executing every action due to failed rules produces less than desirable results.

The approach I took was to create a custom validator that inherits from ValidatorGroup.

    1     public class ShortCircuitingValidatorGroup

    2         : ValidatorGroup

    3     {

    4         public ShortCircuitingValidatorGroup()

    5         {}

    6 

    7         public ShortCircuitingValidatorGroup(string when)

    8             : base(when)

    9         {}

   10 

   11         public ShortCircuitingValidatorGroup(IExpression when)

   12             : base(when)

   13         {}

   14 

   15         /// <summary>

   16         /// The non-shortcircuiting validator group

   17         /// </summary>

   18         public ValidatorGroup GroupToShortCircuit { get; set; }

   19 

   20 

   21         public override bool Validate(object validationContext,

   22                                       System.Collections.IDictionary contextParams,

   23                                       IValidationErrors errors)

   24         {

   25             GroupToShortCircuit.FastValidate = true;

   26             return GroupToShortCircuit.Validate(validationContext, contextParams, errors);

   27         }

   28 

   29     }



As you can see the class holds a reference to the group we want to short circuit and sets the FastValidate property, invoking the behavior I want, to true. The custom validator is setup in xml for the User.UserName property like this

    1 <objects xmlns="http://www.springframework.net"

    2         xmlns:v="http://www.springframework.net/validation">

    3   <v:group id="UserValidator">

    4     <v:validator id="UserNameValidator"

    5                 type="XXX.Infrastructure.Validation.ShortCircuitingValidatorGroup, XXX.Infrastructure">

    6       <v:property name="ValidatorGroup" ref="UserNameValidatorGroup"/>

    7     </v:validator>

    8     ...

    9   </v:group>

   10 

   11   <v:group id="UserNameValidatorGroup">

   12     <v:required id="UserNameRequired" test="UserName">

   13       <v:message id="Errors.UserName.Required"  providers="UserNameError"/>

   14     </v:required>

   15 

   16     <v:regex id="UserNameMinmumComplexity" test="UserName">

   17       <v:property name="Expression" value="^.{5,20}$"/>

   18       <v:message id="Errors.UserName.MinimumComplexity" providers="UserNameError"/>

   19     </v:regex>

   20 

   21     <v:validator id="UserNameIsUnique" test="UserName" type="XXX.Infrastructure.Validation.IsUniqueUserNameValidator, XXX.Infrastructure">

   22       <v:property name="UserRepository" ref="UserRepository"/>

   23       <v:message id="Errors.UserName.IsUnique" providers="UserNameError"/>

   24     </v:validator>

   25   </v:group>

   26   ...

   27 </objects>