Jero Lopez
Jero Lopez

Reputation: 398

ASP.NET MVC "compare" validation doesn't work properly

I have a compare validation for a password - confirm password fields and also a server validation to check if the password fits with a minimum number of characters.

View:

@Html.PasswordFor(model => model.password)
@Html.PasswordFor(model => model.repeatPassword)

Model:

    public class Model_Clerk
    {            
        public int clerkID { get; set; }    

        public string password { get; set; }

        [Compare("password", ErrorMessage = "Error comparing password and password confirm values")]                        
        public string repeatPassword { get; set; }

    }

Controller action method:

    public ActionResult SaveClerk(Model_Clerk model)
    {            
        //Password minimum lenght
        if (!string.IsNullOrEmpty(model.password) && model.password.Trim().Length < 5)
        {
            ModelState.AddModelError(model.password, "Password must be at least 5 characters long");                 
        }

        if (ModelState.IsValid)
        {
            //Save logic here...
        }
        else
        {
            return PartialView("EditClerks", model);
        }
    }

When the server validation is executed the warning message appears correctly, and after that the compare validation will not work anymore. Any idea?

Upvotes: 1

Views: 3578

Answers (2)

Ryan Faricy
Ryan Faricy

Reputation: 96

Ran across this issue today and wrote a CompareIf custom attribute. It's nearly identical to RequiredIf (covered here), but inherits CompareAttribute instead and accepts the otherProperty as a third argument.

using System.ComponentModel.DataAnnotations;

public class CompareIfAttribute : CompareAttribute
{
    private readonly string _dependentProperty;
    private readonly object _targetValue;

    public CompareIfAttribute(string dependentProperty, object targetValue, string otherProperty) : base(otherProperty)
    {
        _dependentProperty = dependentProperty;
        _targetValue = targetValue;
    }

    /// <summary>
    ///     Returns if the given validation result is valid. It checks if the RequiredIfAttribute needs to be validated
    /// </summary>
    /// <param name="value">Value of the control</param>
    /// <param name="validationContext">Validation context</param>
    /// <returns></returns>
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var field = validationContext.ObjectType.GetProperty(_dependentProperty);

        if (field != null)
        {
            var dependentValue = field.GetValue(validationContext.ObjectInstance, null);

            if (dependentValue == null && _targetValue == null || dependentValue?.ToString() == _targetValue?.ToString())
            {
                var test = base.IsValid(value, validationContext);
                
                if (test != ValidationResult.Success)
                {
                    return test;
                }
            }

            return ValidationResult.Success;
        }

        throw new ValidationException($"CompareIf Dependant Property {_dependentProperty} does not exist");
    }
}

First two arguments are the dependent property and target value, and the third is the property to compare with. Usage:

    public bool CandidateHasNoLegalMiddleName { get; set; }

    public string CandidateMiddle { get; set; }

    [CompareIf(nameof(CandidateHasNoLegalMiddleName), false, nameof(CandidateMiddle)]
    public string ConfirmMiddle { get; set; }

This will allow the comparison to occur only if the condition is true.

Upvotes: 0

Tim B James
Tim B James

Reputation: 20364

From our comments, I think that actually the best solution would be to write your own DataAnnotation.

Something like [CompareIf("password", ErrorMessage = "Error comparing password and password confirm values")]

Your DataAnnotation Code would have to check to see if the Password is not empty and valid, and then compare the two values.

Upvotes: 1

Related Questions