6

Update after Bounty was awarded

A new solution is coming up to this problem. Please refer to ASP.NET MVC 3 Preview 1 here: http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx

Look in the section Model Validation Improvements, where you will see the solution to my problem.


Original Post

Referring to my earlier post How to validate two properties with ASP.NET MVC 2 where I asked how I could compare two properties for Model validation.

I did find the answer useful, but I was left with an entirely different problem:

Problem: If a Property-level ValidationAttribute contains an error, then the Class-level ValidationAttributes are NOT validated.

Please consider the following:

[EqualTo("Email", "EmailConfirm", ErrorMessage = "E-mailadresserne skal være ens")]
[EqualTo("Password", "PasswordConfirm", ErrorMessage = "Adgangskoderne skal være ens")]
[Bind(Exclude="UserId")]
public class EditSiteUser
{
    [Required(ErrorMessage="Du skal bekræfte adgangskode")]
    public string PasswordConfirm { get; set; }

    [Required(ErrorMessage="Du skal bekræfte e-mailadressen")]
    [Email(ErrorMessage="Ugyldig e-mailadresse")]
    public string EmailConfirm { get; set; }
    public int UserId { get; set; }

    [Required(ErrorMessage = "Du skal indtaste et brugernavn")]
    public string Username { get; set; }

    [Required(ErrorMessage = "Du skal indtaste en adgangskode")]
    public string Password { get; set; }

    [Required(ErrorMessage = "Du skal indtaste en e-mailadresse")]
    [Email(ErrorMessage = "Ugyldig e-mailadresse")]
    public string Email { get; set; }
}

Here I have two Class-level attibutes that validate EmailConfirm and PasswordConfirm.

If a field like Username is empty, and thus yields an error, then the two EqualTo Attributes are never validated.

Does anyone have a suggestion to overcome this problem?

EDIT: If you need anymore information about this problem, please ask in comments and I will be very happy to give you any additional information you need.

Questions:

Q: "Why is it important that the class-level checks get validated if a property-level check fails?".

A: "Because this is part of a form, where a user enters information into a form that posts back via AJAX. When the form returns it should show all current problems."

Q: "What exactly is the EqualTo attribute you have placed on the class? Is it a custom validation attribute? If so, how does it work? what does it do?"

A: EqualTo is a class-level ValidationAttribute that compares the value of two Properties of the class-instance. Look up "PropertiesMustMatchAttribute" for a similar implementation.

3
  • 1
    Why is it important that the class-level checks get validated if a property-level check fails? Commented Jun 28, 2010 at 18:02
  • I will edit the post with your questions, and supply an answer below. :-) Commented Jun 28, 2010 at 18:06
  • Ok, next question. What exactly is the EqualTo attribute you have placed on the class? Is it a custom validation attribute? If so, how does it work? what does it do? Commented Jun 28, 2010 at 18:09

3 Answers 3

5
+225

This isn't supported. If any of the property level validations fail, then the class level validations are not performed. I suggest you look at MVC Foolproof Validation. It extends MVC validation to add support for contingent property validation. I think that would solve the problem for this particular case.

The project site states that it doesn't work with the MVC2 RC, so you'll have to download the source code and get it running/adopt their ideas yourself.

Sign up to request clarification or add additional context in comments.

5 Comments

This looks very interesting so far Jamie. I've also seen a Fluent Validation framework that would solve my problem. So from the looks of it, it would be a mistake to put validationattributes on the class, if there are any validationattributes on the properties. And moreover the framework seems to provide a nice interface for doing the jQuery validation I have also implemented.
I wouldn't say it's a mistake to put validation attributes on a class because every situation is different. I would say that it is preferable to put them on properties when possible so that the user has a better experience.
I would second Jamie on that one. Better yet, use the 'buddy class' method to separate your data class and the validation logic.
And yet again a bounty that shoulda have been awarded fails to get awarded ... The new rules suck.
I got 1/2 and it pushed me over 10k so no complaints. I think I will follow up on meta though; auto award should award the whole amount if the eligible answer is the accepted answer.
1

To expand on the link to Scott Guthrie's article, starting with MVC 3, you can perform class level validation by implementing the IValidatableObject Interface.

This will work inside the context of the current validation pipeline so it can be nested on as many custom classes and properties as you like and continue to return the full array of possible error messages.

For your class, ditch the class level attributes, and add a method called Validate like this:

public class EditSiteUser : IValidatableObject
{
    public int UserId { get; set; }

    [Required(ErrorMessage = "Du skal indtaste et brugernavn")]
    public string Username { get; set; }

    [Required(ErrorMessage = "Du skal indtaste en adgangskode")]
    public string Password { get; set; }

    [Required(ErrorMessage="Du skal bekræfte adgangskode")]
    public string PasswordConfirm { get; set; }

    [Required(ErrorMessage = "Du skal indtaste en e-mailadresse")]
    [Email(ErrorMessage = "Ugyldig e-mailadresse")]
    public string Email { get; set; }

    [Required(ErrorMessage="Du skal bekræfte e-mailadressen")]
    [Email(ErrorMessage="Ugyldig e-mailadresse")]
    public string EmailConfirm { get; set; }


    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    { 
      // put whatever class level validation you want here

      if (Email !== EmailConfirm)
      {
          yield return new ValidationResult("E-mailadresserne skal være ens", new[] {"EmailConfirm"})
      }

      if (Password !== PasswordConfirm)
      {
          yield return new ValidationResult("Adgangskoderne skal være ens", new[] {"PasswordConfirm"})
      }
    }

}

You can continue to yield return as many validation messages as you'd like.

And you can display them all on the client with @Html.ValidationSummary

If you'd like the message to appear alongside a particular control, the ValidationResult constructor takes an overload with the memberNames of the affected properties, and you can provide the validation message for that particular property with the ValidationMessageFor HTML helper like this:

@Html.ValidationMessageFor(Function(model) model.TestOne )

Also, it's worth mentioning that you can use the CompareValidator to easily ensure the values of two different properties are equal. The upside to using this annotation is that it automatically knows how to enforce this on the client as well as the server, whereas adding IValidatableObject will only run on the server.

[DataType(DataType.Password)]
public string Password { get; set; }

[Compare("Password")]
[DataType(DataType.Password)]
public string ConfirmPassword { get; set; }

For further reading, Scott Guthrie another post with more detail on class level validation.

1 Comment

Very nice and simple class validation for rules involving multiple properties. Unlike Class targeted ValidationAttributes, it allows you to draw attention to the properties involved in the problem. Thank you for sharing.
0

From your example, that you like to have a "confirm other input box entry" boxes, the correct implementation would be

 [EqualTo("Email", ErrorMessage = "E-mailadresserne skal være ens")]
 public string EmailConfirm { get; set; }

as the "error" is a validation of the Confirm box. or in other words, you would like to have the Error message next to the confirm box, saying its not the same as in the Email box.

this puts the validation configuration back to the property, and solving your issue.

2 Comments

I thought property-level validationattributes only have access to its own value?
Ahh, see Jamie Ide's answer. Need MVC Foolproof validation found here: foolproof.codeplex.com

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.