Validation just wrong!

Topics: Validation Application Block
Oct 5, 2007 at 3:10 PM
I am using this against a property

IgnoreNulls(Ruleset = "Update")
StringLengthValidator(1, 5, Ruleset = "Update")
TypeConversionValidator(typeof(int), MessageTemplate = "Employee Number must be numeric", Ruleset = "Update")
IgnoreNulls(Ruleset = "Insert")
StringLengthValidator(1, 5, Ruleset="Insert")
TypeConversionValidator(typeof(int), MessageTemplate="Employee Number must be numeric", Ruleset = "Insert")
public string UserStaffNumber { get; set; }

Basically I'm try to create 2 rulesets per database entity so I can validate a new/updated record before I try and post it to the database. I create these with Codesmith.

So I create 2 rulesets ("Insert" and "Update"). They differ by the way they check the "key" and "timestamp" for being present or not.

Now for the property above it's quite simple. It is a varchar field of 5 characters and the value entered should be an integer.

So I validate using the "Insert" ruleset and I get an "OR" error with 2 nested errors if the field contains "FFF".

I get "Field Must be NULL" and "Employee number must be numeric"

What is that first error all about and what is the "must be null"? That's just wrong!

Also you can see why I would like the "anon" ruleset to mean "all" because it would half the number of attributes!! I have to duplicate every attribute on every field because the "key" and "timestamp" columns need different processing!

Cliff

PS. Are these newsgroups supported? 3 messages and no replies!!!
Oct 5, 2007 at 4:03 PM


charker wrote:
I am using this against a property

IgnoreNulls(Ruleset = "Update")
StringLengthValidator(1, 5, Ruleset = "Update")
TypeConversionValidator(typeof(int), MessageTemplate = "Employee Number must be numeric", Ruleset = "Update")
IgnoreNulls(Ruleset = "Insert")
StringLengthValidator(1, 5, Ruleset="Insert")
TypeConversionValidator(typeof(int), MessageTemplate="Employee Number must be numeric", Ruleset = "Insert")
public string UserStaffNumber { get; set; }

Basically I'm try to create 2 rulesets per database entity so I can validate a new/updated record before I try and post it to the database. I create these with Codesmith.

So I create 2 rulesets ("Insert" and "Update"). They differ by the way they check the "key" and "timestamp" for being present or not.

Now for the property above it's quite simple. It is a varchar field of 5 characters and the value entered should be an integer.

So I validate using the "Insert" ruleset and I get an "OR" error with 2 nested errors if the field contains "FFF".

I get "Field Must be NULL" and "Employee number must be numeric"

What is that first error all about and what is the "must be null"? That's just wrong!

Also you can see why I would like the "anon" ruleset to mean "all" because it would half the number of attributes!! I have to duplicate every attribute on every field because the "key" and "timestamp" columns need different processing!

Cliff

PS. Are these newsgroups supported? 3 messages and no replies!!!


The IgnoreNulls is manifesting itself as a Negated NotNullValidator. It works as expected if the IgnoreNulls is removed, except of course when it's NULL!
Oct 6, 2007 at 2:32 PM
Hi,

The IgnoreNulls really produces an OR between a test for null and the rest of the specified validators. So validation will succeed when either the value is null or all the other validators are successful. The resulting validation value is logically correct, but the default validation messages are indeed confusing. A proper "null filter" validator would have provided a better user experience.

You can work around this by providing your own validation message for the null validation to provide a better indication, like "Value is not null".

Hope this helps,
Fernando
Oct 8, 2007 at 10:47 AM
Fernando

Thanks for the reply. Personally I think an Attribute on each validator

IgnoreNulls=true

would have been the easiest to understand

Cliff
Oct 8, 2007 at 2:53 PM
Edited Oct 8, 2007 at 5:54 PM
OK I sort of tried that but I'm still not sure its working right....

My code looks like this...

IgnoreNulls(MessageTemplate="Value is not NULL", Ruleset = "Insert")
StringLengthValidator(1, 5, Ruleset="Insert")
TypeConversionValidator(typeof(int), Ruleset="Insert")
IgnoreNulls(MessageTemplate="Value is not NULL", Ruleset = "Update")
StringLengthValidator(1, 5, Ruleset="Update")
TypeConversionValidator(typeof(int), MessageTemplate="Should be an integer", Ruleset="Update")
public string UserStaffNumber { get; set; }

I then enter "abc" into the field and validate( "Insert").

I get one error "Value is not NULL" with 2 nested errors
"Value must be null" and "Value must be System.Int32"

Am I going mad? why nested errors?

is it the ordering?

To be honest if i say "Ignore Nulls" I would not expect to get an error about NULL at all. It should be supressed. In this case I would want one error "Should be an integer".

Cliff



fsimonazzi wrote:
Hi,

The IgnoreNulls really produces an OR between a test for null and the rest of the specified validators. So validation will succeed when either the value is null or all the other validators are successful. The resulting validation value is logically correct, but the default validation messages are indeed confusing. A proper "null filter" validator would have provided a better user experience.

You can work around this by providing your own validation message for the null validation to provide a better indication, like "Value is not null".

Hope this helps,
Fernando

Oct 8, 2007 at 3:39 PM
Edited Oct 8, 2007 at 5:55 PM
If I use

TypeConversionValidator(typeof(int), MessageTemplate="Value should be a number", Ruleset="Insert")
public string UserStaffNumber { get; set; }

It work as expected. I get one error "Value should be a number"

If I change to

IgnoreNulls(MessageTemplate="Value is not NULL", Ruleset = "Insert")
TypeConversionValidator(typeof(int), MessageTemplate="Value should be a number", Ruleset="Insert")
public string UserStaffNumber { get; set; }

I get one error "Value is not NULL" and 2 nested errors "Value must be null", "Value should be a number"

Why the nested results?

With

IgnoreNulls(MessageTemplate = "Value is not NULL", Ruleset = "Insert")
StringLengthValidator(1, 5, Ruleset = "Insert")
TypeConversionValidator(typeof(int), MessageTemplate = "Value should be a number", Ruleset = "Insert")
public string UserStaffNumber { get; set; }

I get the same top level error "Value is not NULL" and 2 nested "Value must be null" and "Value should be a number".

How can that be interpetted back to the user? I want to say "Must be integer" and maybe "must be 1..5 characters". If I walk the errors I would have to check I already have a NullValidator "result" and ignore that within the nested results. Seems very hit and miss to me!!

How hard would itbe to have an attribute "AllowNull=true" on every validator? Would that make things easier or harder I ask?

Cliff


Oct 8, 2007 at 6:09 PM

Looking at the code at some point all the vaildators run the "DoValidate" method. This checks to see if the target is NULL and Logs and errors depending upon the validators test.

If an extra attribute was added "IgnoreNull" or "AllowNull" or something, this would be added to the BaseValidationAttribute class i guess?

At some point in the code it must iterate through the tests and decide whether to call DoValidate(). Where is this code? Would it be possible to put in a check before the call to DoValidate which basically says if the object is null and AllowNull is true skip the DoValidate, this unsuring no errors are logged.

Does that make sense? What impact does this have on the config code?

We are currently thinking of dropping the use of validators because it seems a very confusing and difficult solution to a very simple-to-code problem. To validate of object you have a list of if-else conditions and even the most novice developer can code that.

I suppose the approach is to write SelfValidator code and write the code in that method. It does make my job more difficult as I am trying to generate code to validate objects and these attributes "should" be a nice easy way to do it.

After all I just want to create properties for database columns. I want to check varchar fields for length. I want to check NOT NULL columns for not being null. Finally I want to allow a varchar holding a number to be checked for being a number.

When the user enter data I want errors :

You must enter 1..6 characters : if they enter any characters at all (but not 1-6 of them)
The value you enter must be a number : if they enter any characters at all

and if they enter nothing that is OK too because the database allows NULLS on this column.

I would have thougth that was a basic key scenario :)

Cliff
Nov 10, 2009 at 6:45 PM

It is a basic scenario, and that's why I don't understand how anyone can really use this block for anything real. Has anything change with this since the 2007 post?

It seems trying to use the attributes for anything beyond the most simple case ends up being impossible or more confusing than hand coding it, and using the config file for validations gets ugly fast. Add the lack of real validation inheritance and its basically a crippled way of validating things. For me the SelfValidation route is the way to go, unfortunately the PropertyProxyValidator doesn't work with those, so if you want to show the errors next to the field that caused it, forget about that too.... lets not get into the bugs with nulls as mentioned above....

So in the end, I end up writing (or generating) a bunch of <selfvalidation> methods and doing my own thing for showing the errors to the user.... so how exactly does this block help us again ?! Oh yeah it gives us a handy ValidationResults class... (with no constructor that has optional Target, Key, Tag, and Validator parameters) - augh!