[Range(0,double.MaxValue,ErrorMessage="Please specify a value greater than or equal to zero.")]
After publishing the web site to the staging server, I got an email from the client saying that 0 was not a valid value for some of the properties. My first thought was simply to change my range attribute this way:
Range(0.00000000001,double.MaxValue,ErrorMessage="Please specify a value greater than or equal to zero.")]
After all, who's going to type that many zeros? I still wasn't happy with that solution, so I started considering my alternatives. IDataErrorInfo was a definite option, and that would look this:
public string this[string propertyName]
if (NonZeroNumericProperty == 0) return "Please enter a value greater than zero";
Notice that I only checked for a value equal to zero, as the Range attribute still makes sure that the value isn't less than zero. I have two problems with this approach: the first is that my validation for this property is sitting in two different places. The second is that the "this" property from IDataErrorInfo gets really long and ugly really fast. Sure, sometimes there is no better way, but in this case, I think I found one that is both elegant and lets me keep my validation rules together near the relevant property where it can be seen and easily changed.
So I created a new custom validation attribute called NonZeroAttribute that makes sure the property's value isn't zero. Combined with the [Range] attribute, I get everything I wanted.
/// Ensures that an object's value is not equal to zero.
public class NonZeroAttribute : ValidationAttribute
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
ErrorMessage = "Please enter a value that is not equal to zero.";
var badResult= new ValidationResult(this.ErrorMessageString) ;
if (value == null) return null;
double x = 0;
if (double.TryParse(value.ToString(), out x) == false)
ValidationResult result = value.Equals(0.0) ? badResult: null;
The function first checks to see if the value is null. If it is, we let it go. We leave it up to the coder to use a [Required] attribute if necessary. Then we try to parse the object's value into a double. I chose a double because any numeric value should be able to be converted into a double safely. If the TryParse fails, the value is non-numeric. In that case, we return a bad result. I may have to rethink that part, because a non-numeric value is clearly "not zero." Finally, the parsed numeric value is compared to 0 and the result returned. So far, it works like a charm.
With that class in place, I can easily remove my IDataErrorInfo code above and annotate the properties with the appropriate attribute(s);
public double NonZeroProperty
I'd be curious to hear others' thoughts on the pros and cons to this approach, as well as any optimizations I could make.