4

I've trying to validate a property on a model I have. This property is NOT required, and so if its invalid MVC seems to be ignoring it. I've even created a custom ValidationAttribute, but nothing works.

public class NumberWang : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        if (value == null)
            return true;

        int g;
        if (int.TryParse(value.ToString(), out g))
        {
            if (g >= 0)
                return true;
        }
        return false;
    }
}

public class MyModel 
{
    [Range(0, 999999, ErrorMessage = "category_id must be a valid number")]
    [NumberWang(ErrorMessage = "That's NumberWang!")]
    public int? category_id { get; set; }

    /* there are other properties of course, but I've omitted them for simplicity */

    public void Validate()
    {
        Validator.TryValidateProperty(this.category_id,
        new ValidationContext(this, null, null) { MemberName = "category_id" },
        this.validation_results);
    }
}

If I pass the value 'abc' as a category_id to this model, it validates just fine. What am I doing wrong?

4 Answers 4

5

I found an ugly workaround.

It seems that if category_id is a nullable int? and my value is not a valid number, a null value is passed, and the model doesn't see the invalid 'abc' value.

[Range(0, 999999, ErrorMessage = "category_id must be a valid number")]
public int? category_id { get; set; }

// when we pass a good number
MyAction?category_id=123
validation: successful

// when we pass a bad number
// validation ignores it. not what we want. 
MyAction?category_id=abc
validation: successful

If I change category_id to a non-nullable int, it fails validation even when no value is passed.

[Range(0, 999999, ErrorMessage = "category_id must be a valid number")]
public int? category_id { get; set; }

// when we pass a good number
MyAction?category_id=123
validation: successful

// when we pass an bad number
MyAction?category_id=abc
validation: "category_id must be a valid number"

// BUT, when we don't pass any number at all ... 
MyAction 
validation: "category_id must be a valid number"

The Ugly Workaround

If I change category_id to a string, and then only convert it to an int when I need it, I can validate it properly, using only [Range]

[Range(0, 999999, ErrorMessage = "category_id must be a valid number")]
public string category_id { get; set; }

// when we pass a good number
MyAction?category_id=123
validation: successful

// when we pass a bad number
MyAction?category_id=abc
validation: "category_id must be a valid number"

// no number, no validation. hooray!
MyAction 
validation: successful

It's ugly, but it works.

(Note: the custom attribute was not needed, so I removed it and just used [Range])

Sign up to request clarification or add additional context in comments.

1 Comment

1st thing you upper boundary of the Range validator should be set to int.MaxValue. This will ensure that number greater than the maximum int value will fail validation. Back to your original problem, you are doing something wrong since Range attribute should totally block any entry which is not an int (except null of course). Do you maybe have a custom model binder defined that converts your string to a null value? I mean this works just fine for me...
1

model:

// "(\s*[0-9]{0,6})" for 999999 max
// "(\s*[0-9]*)" for "infinite"
[RegularExpression(@"(\s*[0-9]{0,6})", ErrorMessage = "Field must be a natural number (max 999999)")]
public int? Quantity { get; set; }

view:

@Html.TextBoxFor(m => m.Quantity)
@Html.ValidationMessageFor(m => m.Quantity)

Comments

0

First your model should implement IValidatableObject, but the Validate method is going to be called only if the ModelState is valid. This link can help. Are you receiving your model as a parameter in the action? Are you asking for the ModelState.IsValid? This should work fine with the Default model binder.

2 Comments

I run Validate() at the start of my action, and it does validate the other properties. If I put a [Required] attribute on category_id it will validate as missing if not included. But without [Required], it seems to just ignore it completely!
EDIT: Actually scratch that. If I add [Required] and pass in a string it seems to not see the value. I'll just work around it I guess
-1

I found a workaround that I kind of like. The problem I ran into was a similar issue, where the value had to be greater than 0 and a number, so once I tried to cast the 9999999999 (invalid) string as a number it threw an exception and didn't show that the model state has an error message on the post. I hope this is on topic since it sounds like "Rules don't seem to apply correctly when I type in an invalid number"

Since my number had to be positive I intercepted the value into an anonymous type object (dynamic) and used it as an intercepting body between the model's user and the private int property. When I did that, the Data Annotations worked as expected.

    public dynamic MyProperty
    {
        get { return _myProperty; }
        set
        {
            try
            {

/I have to convert the value to String because it comes in as String[], and if there is more than one value then it should be problematic, so you join it together with an invalid character and throw an error, or you take the first value/ _myProperty = = Convert.ToInt32(String.Join("a",value)); } catch (Exception e) { _myProperty= -1; } } }

    private int _myProperty;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.