There are two stages of validation at play here.
Before the validation set up in your attributes is called, the framework first attempts to parse the information.
So here are a couple of examples based on this code:
[Range(0, Int32.MaxValue, ErrorMessage="Invalid Number")]
public int? Number { get; set; }
I type nothing into the box...
"Invalid Number" (Framework will create a null integer, your validation rule fails)
I type "A" into the box...
"The value 'A' is not valid for Number." (Framework cannot convert "A" into a nullable int, so the framework validation rule fails and your validation rule it not checked.
** Solutions **
1 - Live with the default message until you are using MVC 3 / .NET 4, which makes it easier to override these messages
2 - Exclude the value from the binder, so it won't cause an error (but you will have to bind it and check it yourself)
[Bind(Exclude="MyNumber")]
3 - Make it a string on the model, then test it with a TryParse and add your own custom model error (This is a reasonable practice and reminds us all why View Models are used rather than Domain Objects!)
if (!Int32.TryParse("MyNumber", out myInteger)) {
ModelState.AddModelError("MyNumber", "That isn't a number!");
}
There are actually lots of solutions, but I would say go with option 3 for now.