Custom Data Annotations With MVC: How to Check Multiple Properties at One Time

Ok so maybe you read this post and you’re thinking to yourself, “I wonder if Diet Dr. Pepper really does taste like regular Dr. Pepper” which of course is silly because the ads clearly say it does. Now you should be asking yourself, “How do I check if two properties have the same value on a class with annotation when the property level ones only can check the one property’s value?” Kind of wordy way of asking that, but I get what you’re asking. Better yet, like usual, I have an answer.

Now what I can swear is that I was no where near the Baxter Building between 1-3 am on Saturday the 15th. I was in fact I was sleeping and have 20 witnesses that can testify. What I can’t swear is this is the best way to go about it but it seems to work AND make sense. It’s not common for me to come up with solutions that do both.

  [AttributeUsage(AttributeTargets.Class)]
  public class PropertiesMatchAttribute : ValidationAttribute
  { 

    public String FirstPropertyName { get; set; }
    public String SecondPropertyName { get; set; } 

    //Constructor to take in the property names that are supposed to be checked
    public PropertiesMatchAttribute(String firstPropertyName, String secondPropertyName )
    {
      FirstPropertyName = firstPropertyName;
      SecondPropertyName = secondPropertyName ;
    } 

    public override Boolean IsValid(Object value)
    {
      Type objectType = value.GetType();
      //Get the property info for the object passed in.  This is the class the attribute is
      //  attached to
      //I would suggest caching this part... at least the PropertyInfo[]
      PropertyInfo[] neededProperties =
        objectType.GetProperties()
        .Where(propertyInfo => propertyInfo.Name == FirstPropertyName || propertyInfo.Name == SecondPropertyName)
        .ToArray();

      if(neededProperties.Count() != 2)
      {
        throw new ApplicationException("PropertiesMatchAttribute error on " + objectType.Name);
      }

      Boolean isValid = true;

      //Convert both values to string and compare...  Probably could be done better than this
      //  but let's not get bogged down with how dumb I am.  We should be concerned about
      //  dumb you are, jerkface.
      if(!Convert.ToString(neededProperties[0].GetValue(value, null)).Equals(Convert.ToString(neededProperties[1].GetValue(value, null))))
      {
        isValid = false;
      }

      return isValid;
    }
  }
}

And then the use:

  [PropertiesMatchAttribute("Password", "ConfirmPassword", ErrorMessage = "Match the passwords, dumb--s.")]
  public class UserCreateModel
  {
     public String ConfirmPassword{ get; set; }
     public String Password { get; set; }
  }

Wasn’t so hard was it? Normally this would be the part where I degrade you for being bumb because you didn’t figure this out but I’m above that now. I guess it must be the holiday season or that I’ve been told by the board that I should be more gentle with my readers. Guess some were crying after reading some of my posts. Yeah whatever. Bunch of [Removed by Andre].

Tags: ,

  • wedfsdfd

    what a character you are

  • Abhishek

    Hi,

    Great Article!!

    But, how would you handle a scenario, where in you have validators both at class as well as property level?

  • http://www.byatool.com Sean

    You mean as in an attribute that could be used for both? That’s a good questions and didn’t really get to that yet in my use. There might be a way within the attribute to check to see if it is assigned to a class or property, but not sure.

  • astacy

    I’ve implemented the same solution for password and email confirmation. I try to use two PropertiesMatchAttribute attributes on the class, but only one is validated. I do have AllowMultiple = true in the AttributeUsageAttribute. Any ideas?

    Thanks.

  • Mohammad

    Nice Article. I wonder how the client validation works in this case since Html.ValidationMessageFor is expecting one data member of the model.

    • http://www.byatool.com Sean

      You know, this was actually a point of contention the last place I worked. I personally don’t use helper methods for form building, and as for validation I hand produce jQuery Validation. I realize the issue with a possible disconnect with the model’s annotations and the jQuery Validation rules, but I just can’t stand having to jack my form up to conform to the gooney way Microsoft expects the form to look. Especially since the way it’s implemented right now is dubious at best.

  • erx

    the funny thing is, not only are you rude to your audience, but you actually DID NOT WRITE THAT and this is available in the default project created by mvc 2, so if you load up VS and create new project mvc 2 application (not empty project but templated one) the default project that loads up is where it is. if you search for PropertiesMustMatchAttribute you will find this exact implementation – you just copied the code from there and added some comments to you – you are SUCH A LOSER!!

    • http://www.byatool.com Sean

      Good thing you know exactly what I have and have not written. Fact is I did hand churn this guy out for work. If you want to accuse me of anything, accuse me of being an idiot for not taking the 2 seconds to find out I was about to re-invent the wheel. I created this little gem because the at time built in annotations did nothing for me. Chances are the reason this may look like someone else’s implementation is because there’s only so many ways to do this.

      And if you’re implying that something as simple as checking a class for properties and their values is beyond my grasp, I’d be hurt and I don’t think we could be friends. And I want to be your friend.

  • erx

    if slagging off your users was indeed your sense of humor i am sorry, but heck its hard to tell!!

    + please dont say you figured it out when you copied almost all of it :) take care.

    • http://www.byatool.com Sean

      I do it because I care.

  • Gini

    Nice article. I also created PostalCodeValidator using multiple fields. Worked fine until I move my entity into a user control. The “IsValid” is not fired anymore…. any idea?

  • SJ

    Hi Sean

    I tried using your code, and it seems to work fine. I have a problem though — maybe you have a suggestion for solving it?

    When I apply this ValidationAttribute to a class that has more ValidationAttributes on the property-level, and one of those fails, then the class-level ValidationAttribute is not validated eventhough it contains errors.

    Have you found a way to solve this problem ?

    • http://www.byatool.com Sean

      So you mean it picks up on the property level issues only. Does it go onto the class level ones once that those issues are resolved? Been a while since I’ve used this.

  • SJ

    Yes. Once the property-level issues are solved it does check the Class-level attributes.

    • http://www.byatool.com Sean

      Yeah I think I remember that happening, but as I said it’s been a while and I’ve been stuck in 2.0 land for the last 4 months. I don’t think I had a solution for that at the point I made this. If I get all sorts of ambitious, I might see if can dust this off and figure it out. My guess though is this is by design. Each attribute carries a Valid flag and I wouldn’t doubt that it’s designed to roll through the property level validation attributes and fail if any of those is invalid, thus ignoring class level ones.

  • http://www.facebook.com/profile.php?id=1732911838 Luca Domenichini

    Is it possibile to customize the error message with — let’s say — the actual value of a property.
    I mean, can I customize it programmatically!

    • http://www.byatool.com ByATool

      I guessing you mean change the error message within the attribute’s IsValid method?

  • http://twitter.com/edusfc15 Eduardo Santana

    Perfect. Thanks a lot for article.

    • http://www.byatool.com ByATool

      You are welcome. You and you only.

  • Guest

    Thank you very much ! Nice one