132

On my MVC3 project, I store score prediction for football/soccer/hockey/... sport game. So one of properties of my prediction class looks like this:

[Range(0, 15, ErrorMessage = "Can only be between 0 .. 15")]
[StringLength(2, ErrorMessage = "Max 2 digits")]
[Remote("PredictionOK", "Predict", ErrorMessage = "Prediction can only be a number in range 0 .. 15")]
public int? HomeTeamPrediction { get; set; }

Now, I need also change error message for a data type, int in my case. There is some default one used - "The field HomeTeamPrediction must be a number.". Need to find a way how to change this error message. This validation message also seem to take prediction for Remote validation one.

I've tried [DataType] attribute but this does not seem to be plain number in system.componentmodel.dataannotations.datatype enumeration.

10 Answers 10

257

For any number validation you have to use different a different range validation as per your requirements:

For Integer

[Range(0, int.MaxValue, ErrorMessage = "Please enter valid integer Number")]

for float

[Range(0, float.MaxValue, ErrorMessage = "Please enter valid float Number")]

for double

[Range(0, double.MaxValue, ErrorMessage = "Please enter valid doubleNumber")]
Sign up to request clarification or add additional context in comments.

2 Comments

This didn't work for me in my context. If the user enters "asdf", [Range(typeof(decimal), "0", "9999.99", ErrorMessage = "Value for {0} must be between {1} and {2}")] throw an exception. However, if I do [Range(typeof(decimal), "0.1", "9999.99", ErrorMessage = "Value for {0} must be between {1} and {2}")] , the error message will work correctly. 0 vs 0.1 , makes no sense. bug maybe?
This "integer" validation treats non-integer values as valid (e.g. 0.3)
92

Try one of these regular expressions:

// for numbers that need to start with a zero
[RegularExpression("([0-9]+)")] 


// for numbers that begin from 1
[RegularExpression("([1-9][0-9]*)")] 

hope it helps :D

4 Comments

Is there not a simpler way? I would hope for something like: [Numeric(ErrorMessage = "This field must be a number")]
Unfortunately no. You can always write your own validation attribute.
This is the better solution, since this covers strings. int.MaxValue only covers until 2.147.483.647
This regex will only work if the property is of type int, if you're using a property of type string the regex it will also accept a123 or any other string that has at least a number somewhere. to validate a number from beginning to end, use ^[0-9]+$ for integers or ^[0-9]*\.?[0-9]+$ for floats
25

Use regex in data annotation

[RegularExpression("([0-9]+)", ErrorMessage = "Please enter valid Number")]
public int MaxJsonLength { get; set; }

3 Comments

This seems to work fine in the context of the question, provided that the property is not int, but string.
Why the parenthesis around the regular expression? Could it be just [0-9]+?
This regex will only work if the property is of type int, if you're using a property of type string the regex it will also accept a123 or any other string that has at least a number somewhere. to validate a number from beginning to end, use ^[0-9]+$ for integers or ^[0-9]*\.?[0-9]+$ for floats
9
public class IsNumericAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value != null)
        {
            decimal val;
            var isNumeric = decimal.TryParse(value.ToString(), out val);

            if (!isNumeric)
            {                   
                return new ValidationResult("Must be numeric");                    
            }
        }

        return ValidationResult.Success;
    }
}

Comments

6

Try this attribute :

public class NumericAttribute : ValidationAttribute, IClientValidatable {

    public override bool IsValid(object value) {
        return value.ToString().All(c => (c >= '0' && c <= '9') || c == '-' || c == ' ');
    }


    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = FormatErrorMessage(metadata.DisplayName),
            ValidationType = "numeric"
        };
        yield return rule;
    }
}

And also you must register the attribute in the validator plugin:

if($.validator){
     $.validator.unobtrusive.adapters.add(
        'numeric', [], function (options) {
            options.rules['numeric'] = options.params;
            options.messages['numeric'] = options.message;
        }
    );
}

Comments

4

I was able to bypass all the framework messages by making the property a string in my view model.

[Range(0, 15, ErrorMessage = "Can only be between 0 .. 15")]
[StringLength(2, ErrorMessage = "Max 2 digits")]
[Remote("PredictionOK", "Predict", ErrorMessage = "Prediction can only be a number in range 0 .. 15")]
public string HomeTeamPrediction { get; set; }

Then I need to do some conversion in my get method:

viewModel.HomeTeamPrediction = databaseModel.HomeTeamPrediction.ToString();

and post method:

databaseModel.HomeTeamPrediction = int.Parse(viewModel.HomeTeamPrediction);

This works best when using the range attribute, otherwise some additional validation would be needed to make sure the value is a number.

You can also specify the type of number by changing the numbers in the range to the correct type:

[Range(0, 10000000F, ErrorMessageResourceType = typeof(GauErrorMessages), ErrorMessageResourceName = nameof(GauErrorMessages.MoneyRange))]

Comments

3

You can write a custom validation attribute:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class Numeric : ValidationAttribute
{
    public Numeric(string errorMessage) : base(errorMessage)
    {
    }

    /// <summary>
    /// Check if given value is numeric
    /// </summary>
    /// <param name="value">The input value</param>
    /// <returns>True if value is numeric</returns>
    public override bool IsValid(object value)
    {
        return decimal.TryParse(value?.ToString(), out _);
    }
}

On your property you can then use the following annotation:

[Numeric("Please fill in a valid number.")]
public int NumberOfBooks { get; set; }

Comments

1

almost a decade passed but the issue still valid with Asp.Net Core 2.2 as well.

I managed it by adding data-val-number to the input field the use localization on the message:

<input asp-for="Age" data-val-number="@_localize["Please enter a valid number."]"/>

Comments

1

ASP.NET Core 3.1

This is my implementation of the feature, it works on server side as well as with jquery validation unobtrusive with a custom error message just like any other attribute:

The attribute:

  [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public class MustBeIntegerAttribute : ValidationAttribute, IClientModelValidator
    {
        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            var errorMsg = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
            MergeAttribute(context.Attributes, "data-val-mustbeinteger", errorMsg);
        }

        public override bool IsValid(object value)
        {
            return int.TryParse(value?.ToString() ?? "", out int newVal);
        }

        private bool MergeAttribute(
              IDictionary<string, string> attributes,
              string key,
              string value)
        {
            if (attributes.ContainsKey(key))
            {
                return false;
            }
            attributes.Add(key, value);
            return true;
        }
    }

Client side logic:

$.validator.addMethod("mustbeinteger",
    function (value, element, parameters) {
        return !isNaN(parseInt(value)) && isFinite(value);
    });

$.validator.unobtrusive.adapters.add("mustbeinteger", [], function (options) {
    options.rules.mustbeinteger = {};
    options.messages["mustbeinteger"] = options.message;
});

And finally the Usage:

 [MustBeInteger(ErrorMessage = "You must provide a valid number")]
 public int SomeNumber { get; set; }

Comments

0

Another simple solution that will plug into the built in framework, including client side validation by extending the System.ComponentModel.DataAnnotations.RegularExpressionAttribute. Use a couple of constansts for the regular expressions for allowing 0+, or allow 1+ with a bool flag.

namespace MyCustomNamespace.MyCustomAttributes {
    public class ValidPositiveIntegerAttribute : System.ComponentModel.DataAnnotations.RegularExpressionAttribute {
         
        private const string defaultErrorMessage = "* invalid integer value";
        private const string onePlusRegex = @"(^[1-9]*\.?[0-9]+$)";
        private const string zeroPlusRegex = @"(^[0-9]*\.?[0-9]+$)";     
            
        public ValidPositiveIntegerAttribute(string errorMessage, bool allowZero=false) : base(allowZero ? zeroPlusRegex : onePlusRegex) {
            this.ErrorMessage = errorMessage ?? defaultErrorMessage;
        }
        public ValidPositiveIntegerAttribute(bool allowZero = false) : base(allowZero ? zeroPlusRegex : onePlusRegex) {
            this.ErrorMessage = defaultErrorMessage;
        }       
    }
}

And then on the model you just add the attribute(make sure you have the namespace availiable for the attribute you create:

[ValidPositiveInteger]
public int? HomeTeamPrediction { get; set; }

or to allow Zero

[ValidPositiveInteger(true)]
public int? HomeTeamPrediction { get; set; }

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.