I have a bit of an odd requirement.
I have a class with 2 properties on it as shown below:
public class MyViewModel
{
[EnhancedServiceValidInteger("Name")]
public int NumberOfItems { get; set; }
public string Name { get; set; }
}
The above class is used as a dynamic field used to build dynamic forms. When NumberOfItems is incorrect (not a valid integer), an error message should be displayed using the value from the Name property as the DisplayName and is the output of any error messages.
I've implemented a custom validation attribute that triggers, but if the user enters a letter I want that to be picked up by the attribute. In this case (IsValid(object value, ...)) the IsValid methods value property is always 0 so the validation passes and no breakpoint in MyCustomValidIntegerValidator gets hit. Why doesn't the value get set to 'aaa' if that is what was entered in the UI?
Can anyone help please?
I have the following custom validation attribute:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false)]
public class MyCustomValidInteger : ValidationAttribute
{
private readonly string _validationNameFromProperty;
public EnhancedServiceValidInteger(string validationNameFromProperty) : base("{0} contains invalid character.")
{
_validationNameFromProperty = validationNameFromProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// the the other property
var property = validationContext.ObjectType.GetProperty(_validationNameFromProperty);
// check it is not null
if (property == null)
{
return new ValidationResult(String.Format("Unknown property: {0}.", _validationNameFromProperty));
}
// get the other value
var nameToUse = property.GetValue(validationContext.ObjectInstance, null).ToString();
if (value == null || value.ToString().Length == 0)
{
return ValidationResult.Success;
}
int i;
var errorMessage = string.Format("The value '{0}' is not valid for {1}", value, nameToUse);
ValidationResult validationResult = !int.TryParse(value.ToString(), out i) ? new ValidationResult(errorMessage) : ValidationResult.Success;
return validationResult;
}
}
Here is the DataAnnotationsModelValidator implementation for the above class:
public class MyCustomValidIntegerValidator : DataAnnotationsModelValidator<MyCustomValidInteger>
{
public MyCustomValidIntegerValidator(ModelMetadata metadata, ControllerContext context, EnhancedServiceValidInteger attribute)
: base(metadata, context, attribute)
{
if (!attribute.IsValid(context.HttpContext.Request.Form[metadata.PropertyName]))
{
var propertyName = metadata.PropertyName;
context.Controller.ViewData.ModelState[propertyName].Errors.Clear();
context.Controller.ViewData.ModelState[propertyName].Errors.Add(attribute.ErrorMessage);
}
}
}
EditorFor()Html helper, note that it will create atype="number"input withrequiredas well. Some browsers will NOT return a value if you put letters in it.