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>
1 comments:
Hi,
I've suggested another workaround that would seem to be a bit less invasive, see http://forum.springframework.net/showthread.php?p=15362 for info. Thanks for taking the time to blog about it!!! Much appreciated.
Post a Comment