1

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?

Jero Lopez
  • 398
  • 1
  • 7
  • 18
  • Why not use `[StringLength(5, ErrorMessage = "The password must be at least 5 characters long", MinimumLength = 5)]` On the password property, and also add `[Required]` to both – Tim B James Sep 27 '12 at 09:40
  • Actually I have more servers validations, the fact is that the compare validation seems not to work properly after one of the server validations is executed using the ModelState.AddModelError method. It will always says that the compared values are not the same, even when both fields are empty, somethings breaks in the client side, but I don't know what. Thanks anyway for the tip, I didn't know about the "MinimumLength" in the StringLength annotation. – Jero Lopez Sep 27 '12 at 09:49
  • 1
    I would imagine that because you do not have `[Required]` then it might be causing problems. Add this and give me an update. – Tim B James Sep 27 '12 at 09:51
  • It works with required!, but don't understand why, which is the relation between required and compare?, in my case I don't want to make the password required, I am editing a clerk profile, and the password must be filled only if the user wants to change it, if the user doesn't want to change the password he must only leave it empty and no update action is performed. Any other suggestion? – Jero Lopez Sep 27 '12 at 09:58
  • If you add `[Required]` only to the `repeatPassword` property and not the `password` property, then this should work – Tim B James Sep 27 '12 at 10:05
  • Not really, when I try to submit the form the [Required] validation in the repeatPassword textbox triggers and ask for an input value. ? – Jero Lopez Sep 27 '12 at 10:17
  • 1
    I think in your situation, because the password is no required, then DataAnnotations will not work as you want them to. I think you will have to just stick to manually validating the values in your controller OR have a separate form for changing the password. – Tim B James Sep 27 '12 at 10:20

2 Answers2

1

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.

Tim B James
  • 20,084
  • 4
  • 73
  • 103
  • Yes I think so, it is crazy that the [Compare] validator needs beside a [Required] validator, but seems like that, so your answer is acceptable. – Jero Lopez Sep 27 '12 at 11:04
0

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.