1

I have created a custom validator this way:

public class IntArrayRequiredAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if ((!(value is int[] array) || !array.Any() || array.Any(item => item == 0)))
            return new ValidationResult(this.ErrorMessage);

        return ValidationResult.Success;
    }
}

and apply it to a model property:

    [IntArrayRequiredAttribute(ErrorMessage = "You must select {0}.")]
    [Display(Name = "Rol")]
    public int[] Roles { get; set; }

Well, when the validation fails, this error is shown:

"You must select {0}."

How can I return the error message so that {0} is replaced by the display name of the field automatically, such as the built-in validators?

Expected result should be "You must select Rol."

EDIT:

By Seeing ValidationAttribute source code, I read:

    public ValidationResult GetValidationResult(object value, ValidationContext validationContext) {
        if (validationContext == null) {
            throw new ArgumentNullException("validationContext");
        }

        ValidationResult result = this.IsValid(value, validationContext);

        // If validation fails, we want to ensure we have a ValidationResult that guarantees it has an ErrorMessage
        if (result != null) {
            bool hasErrorMessage = (result != null) ? !string.IsNullOrEmpty(result.ErrorMessage) : false;
            if (!hasErrorMessage) {
                string errorMessage = this.FormatErrorMessage(validationContext.DisplayName);
                result = new ValidationResult(errorMessage, result.MemberNames);
            }
        }

        return result;
    }

I saw that it calls my overridden IsValid method and it formats the message. Why isn't it formatting in my case?

If I use the IsValid overload, it formats correctly, however, I need to use this method because I need validationContext for other validation purpose.

1 Answer 1

1

I don't think the reference source match the real code, as reflection revеаls:

public ValidationResult GetValidationResult(object value, ValidationContext validationContext)
{
    if (validationContext == null)
    {
        throw new ArgumentNullException("validationContext");
    }
    ValidationResult validationResult = IsValid(value, validationContext);
    if (validationResult != null && (validationResult == null || string.IsNullOrEmpty(validationResult.ErrorMessage)))
    {
        string errorMessage = FormatErrorMessage(validationContext.DisplayName);
        validationResult = new ValidationResult(errorMessage, validationResult.MemberNames);
    }
    return validationResult;
}

So if you want to fit everything into the single overload

protected override ValidationResult IsValid(object value, ValidationContext validationContext) {...}

you could let the base class do the ErrorMessage formatting:

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
    if ((!(value is int[] array) || !array.Any() || array.Any(item => item == 0)))
    {
       return new ValidationResult(null);
    }

    return ValidationResult.Success;
}

or you could do the formatting yourself:

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
    if ((!(value is int[] array) || !array.Any() || array.Any(item => item == 0)))
    {
        string errorMessage = FormatErrorMessage(validationContext.DisplayName);
        return new ValidationResult(errorMessage);
    }

    return ValidationResult.Success;
}
Sign up to request clarification or add additional context in comments.

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.