asp.net-mvc-3validationasp.net-mvc-4ivalidatableobject

IValidatableObject not working in mvc 4 web app


So I have web app that is registering users and business partners.

This is how my models look like

public class UserModel : IValidatableObject
{
   //here are some properties and methods that I am using in Validate method        

    public CompanyModel Company { get; set; } //this is user Company

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {       
         if (this.UserWithEmailExists())
             yield return new ValidationResult("Email already exists", new[] { "Email" });

         if (this.UserWithUsernameExists())
             yield return new ValidationResult("Username already exists", new[] { "Username" });
    }
}

public class CompanyModel : IValidatableObject
{
    //again here are some properties that i am using in Validate

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (this.Phone == null && this.Mobile == null)
            yield return new ValidationResult("The Phone field is required", new[] { "Phone" });
    }
}

//this is the model passed to the view
public sealed class RegistrationModel : UserModel, IValidatableObject
{
    public new IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (this.Password != this.PasswordValidation)
            yield return new ValidationResult("Ponovite prejšnji vnos", new[] { "PasswordValidation" });
    }
}

The problem is: When I submit my form Validate in CompanyModel is called and Validate in RegistrationModel too. But I want also Validate in UserModel to be called...

UPDATE: I solved the first problem: These are the models after upgrading :)

public class UserModel : IValidatableObject
{
   //here are some properties and methods that I am using in Validate method        

    public CompanyModel Company { get; set; } //this is user Company

    public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {       
         if (this.UserWithEmailExists())
             yield return new ValidationResult("Email already exists", new[] { "Email" });

         if (this.UserWithUsernameExists())
             yield return new ValidationResult("Username already exists", new[] { "Username" });
    }
}

public class CompanyModel : IValidatableObject
{
    //again here are some properties that i am using in Validate

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (this.Phone == null && this.Mobile == null)
            yield return new ValidationResult("The Phone field is required", new[] { "Phone" });
    }
}

//this is the model passed to the view
public sealed class RegistrationModel : UserModel, IValidatableObject
{
    public override new IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        //this is needed so that UserModel validation executes
        foreach (var result in base.Validate(validationContext))
        {
            yield return result;
        }

        if (this.Password != this.PasswordValidation)
            yield return new ValidationResult("Ponovite prejšnji vnos", new[] { "PasswordValidation" });
    }
}

Now validation works fine. I just do not know why Validate in CompanyModel executes two times??. Any suggestion?


Solution

  • When you override a method in a base class, the method in that base class is not explicitly called. You need to do it yourself, so in the Validate function in RegistrationModel, add a call to the Validate function in the base UserModel class.

    base.Validate(validationContext);
    

    However, as you use yield return in your validators, that makes the Validate function an iterator, so it must be iterated through in order for everything to work properly. As per this question, the full solution is to use this code in your RegistrationModel.Validate function:

    foreach (var result in base.Validate(validationContext))
    {
        yield return result;
    }