MessageTemplate for Composition Validators

Topics: Validation Application Block
Apr 16, 2007 at 9:52 PM
Take this class:

public class Test
{
private DateTime eventDate;
private DateTime registrationOpen;
private DateTime registrationDeadline;

public DateTime EventDate { get{return(this.eventDate);} set{this.eventDate = value;} }
public DateTime RegistrationOpen { get{return(this.registrationOpen);} set{this.registrationOpen = value;} }

PropertyComparisonValidator("EventDate", ComparisonOperator.LessThan, MessageTemplate="Registration Deadline must be before the event date")
PropertyComparisonValidator("RegistrationOpen", ComparisonOperator.GreaterThanEqual, MessageTemplate="Registration Deadline must be greater than or equal to the first day to register")
ValidatorComposition(CompositionType.Or, MessageTemplate="Registration Deadline must be greater than or equal to the first day to register and before the event date")
public DateTime RegistrationDeadline { get{return(this.registrationDeadline);} set{this.registrationDeadline = value;} }
}

If you then run this code:
Test test = new Test();
test.EventDate = DateTime.Now.AddDays(-1);
test.RegistrationOpen = DateTime.Now.AddDays(2);
test.RegistrationDeadline = DateTime.Now;

ValidationResults r = Validation.Validate<Test>(test);
foreach(ValidationResult result in r)
{
Response.Write(result.Message);
}

you get:
Registration Deadline must be greater than or equal to the first day to register and before the event date

So the CompositionType.Or message template was used in place of the individual message templates. This is what I would expect.

However, if I change to CompositionType.And and set the message template, it is ignored. Instead each individual message is shown.

How can I set it so if all conditions fail, then one message is displayed (the message associated with the composition validator), and if only one fails, then it either a) it still uses that composition message or b) shows only the relevant individual message? I don't mind either of these (a or b) when only one fails, but I cannot figure out how to get a combined message instead of multiple individual messages when using CompositionType.And, where you can do this with CompositionType.Or

Is this a bug? If not how can this be accomplished?
Apr 16, 2007 at 10:00 PM
The feature is working as designed, but I can see that it is not necessarily what you would expect. When multiple validators are combined with AND and all fail, each failed validator will add a ValidationResult. But when they are combined with OR, the OrCompositeValidator logs a single result (which contains the nested results).

While the behavior seems inconsistent, we felt it makes logical sense. Suppose we have one validator which checks a string has more than 5 characters, and another validator which checks that a string contains a vowel. If you combine them with AND and both fail, you get 2 results that each describe an issue that would need to be rectified. However if you combined them with an OR, it would be misleading to have 2 results since it is only necessary to change one thing.

If you want a similar behavior with the AndCompositeValidator, you could either build your own specialized version (possibly deriving from the original one, but I haven't tried this), or modify the existing code to behave the way that you want.

Tom
Apr 16, 2007 at 10:11 PM
Okay I see that - it just seems strange that it accepts a message template and then ignores it...
Apr 18, 2007 at 9:03 PM
Tom,

It looks like you can nest the conditions however. So, if you had something that looked like:

Or Composite
   Condition 1
   And Composite
      Condition 2
      Condition 3

I'm using this "pattern" for optional fields that require validation if data is entered. So, Condition 1 will fail (i.e data was keyed in), but the data must still conform to conditions 2 and 3. Using the block this way results in just the one "Or" message.

Is there anyway to mark a validation as optional? Because once you use the OrComposite, you lose the extra validation result messages.

I'm thinking an OptionalCompositeValidator might be useful.

Thanks,
Sergio

P.S. Deriving from the two composite validators isn't easy because the validator collection is private and the one accessor ("for testing purposes") is internal. I think you'd really need that collection to be protected so the derived class can get to the nested validations.