21

In an MVC 5 project, I have a model with a nullable int. For reasons that might not be productive to explain, it needs to be an nullable int and cannot be a string.

// Value can be null or an integer from 0 to 145        
[Range(0,145)]
public int? Criterion { get; set; }

The intended annotation is to provide user feedback when entering a value in a form.

Criterion: @Html.TextBoxFor(m => m.Criterion)
<span class="text-danger">@Html.ValidationMessageFor(m => m.Criterion)</span>

While the user does get feedback when entering non-integer values, the Range attribute does not appear to work.

In order to enforce a nullable integer range, will I need to use regular expressions, or is there a simpler way to enforce the rule?

3 Answers3

25

If I remember correctly, the [Range] data annotation should work as expected for nullable integers (i.e. it will only validate that a number falls within the range if one is present). You'll need to ensure that you are calling ModelState.IsValid within your POST action in order to trigger this server-side validation.

Example

The example below demonstrates using null, -1, 1 and 150 as input values along with their expected results :

enter image description here

You can see an example demonstrating this here.

Rion Williams
  • 74,820
  • 37
  • 200
  • 327
  • You're using a string. My model is a nullable int. –  Jul 12 '16 at 14:42
  • Oops, sorry for the typo. I've updated the example to use an `int?`, it should still work just as expected. – Rion Williams Jul 12 '16 at 14:46
  • I see that it does work in your case. In the case of my BeginForm, I am not posting my ViewModel: I am submitting values used to _filter_ my ViewModel, so I don't think this will apply. –  Jul 12 '16 at 14:54
  • The same basic idea would apply. You don't necessarily need to post to the same ViewModel, you could creating a `FilteringModel` class and set the appropriate attributes on that and handle it that way if it makes more sense. – Rion Williams Jul 12 '16 at 14:56
  • In case someone wonders: Microsoft specifies that the range attribute is not responsible for null values // Automatically pass if value is null or empty. RequiredAttribute should be used to assert a value is not empty. if (value == null) { return true; } https://web.archive.org/web/20230526072105/https://github.com/microsoft/referencesource/blob/master/System.ComponentModel.DataAnnotations/DataAnnotations/RangeAttribute.cs – fuchs777 May 26 '23 at 07:26
10

I know this is old but I had the same situation today, Just in case someone else is having the same problem you need to know that Range does not validate the null value, but you can use it with Required to make the validation possible, it would be:

[Required, Range(0, 145)]
public int? Criterion  { get; set; }

Here's an example based on Rion's fiddle: https://dotnetfiddle.net/SBoqJA

nramirez
  • 5,230
  • 2
  • 23
  • 39
  • This should be marked as the correct answer, since as @nramirez said, `Range` validation attribute doesn't consider the `null` value. Anyway, doing this way there's still the problem that the value has to be filled since it's required. I had the same problem when creating a model for a web API that has a swagger interface. The swagger UI add the mandatory indicator `*` on the field. – Cheshire Cat Mar 22 '22 at 10:42
1

You can create a custom data annotation to do this pretty easily by inheriting ValidationAttribute and IClientValidatable.

public class ValidateNullableInt : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        return IsValid(value != null ? value : null)
    }

    public override ValidationResult IsValid(object value, ValidationContext context)
    {
        bool isValid = false;
        if(typeof(value) == Nullable<int>)
        {
            int? temp = value as Nullable<int>;

            if(temp.HasVaue > minValue && temp.HasValue < maxValue)
            {
                return ValidationResult.Success;
            }

            return new ValidatonResult(errorMessage);
        }
    }
}
Ingenioushax
  • 718
  • 5
  • 20
  • Where would I place those functions? –  Jul 12 '16 at 16:52
  • Create a folder with whatever name you want, I use `DataAnnotationExtensions`. Then, create a class with whatever you want it named `ValidateNullableInt` might work. And once you have it all done, just use it as a regular Data Annotation. Probably need to add a `using myProjectName.DataAnnotationExtensions` to the controller as well. – Ingenioushax Jul 12 '16 at 16:54