1

I'm building an ASP.NET MVC site where I want to decorate my ViewModels with validation attributes. One of the things I want to validate is that the address that a user submits through a form is geocodable. For that, I have already created a custom ValidationAttribute and have applied to my StreetAddress property.

This is all good, except that I am actually making two geocoding requests - one in my ViewModel for validation and another in my Controller to input the latitude and longitude into my database. I want to cut down on unnecessary network usage and delays, so I need to pass the result from the validation geocode into my controller.

To accomplish such a thing, I think I should create a Latitude and Longitude property in my ViewModel. The View itself won't touch these 2 properties, but the validation attribute will either report a failure in geocoding and return the View or write the results into the properties.

For a validation attribute to access 3 properties, it has to be applied to the whole class. I don't know how to do that yet, but that's what this question is for.

UPDATE: Thanks to this answer, I have figured out how to create a class-level validation attribute. The link in the answer also demonstrates how to read the contents of a property inside the class (via Reflection). I still haven't figured out how to write to a property, though.


My Questions

How do I create a ValidationAttribute that can be applied to a whole class? Below, I have posted the code that I want to transfrom into an attribute that can be applied to my ViewModel:

public class GeocodableAttribute : ValidationAttribute
    {
        public GeocodableAttribute() : base()
        {
            ErrorMessage = "We weren't able to find that location.";
        }
        public override bool IsValid(object value)
        {
            if (value == null) //we don't care if it's required or not.
            {
                return true;
            }
            var address = (string)value;
            var response = Geocoder.CallGeoWS(address.Trim());
            if(response.Status=="ZERO_RESULTS")
            {
                return false;
            }
            return true;
        }
    }


How do I have the attribute write to certain properties in the class that it is applied to? When I apply this attribute to my ViewModel, I want it to write successful geocoding results into two properties and return true. How can I implement that?

UPDATE #2: I think I just figured out how to write to a property. Should the following code work?

    private static void WritePropertyValue(object obj, string propertyName, object valueToWrite)
    {
        if (obj == null) return null;
        var type = obj.GetType();
        var propertyInfo = type.GetProperty(propertyName);
        if (propertyInfo == null) return null;
        propertyInfo.SetValue(obj, valueToWrite, null);
    }

Will this break client-side validation for other attributes/properties? I have other properties in my ViewModel that I have decorated with built-in ValidationAttributes, such as [Required] and [Range]. I have also enabled client-side validation for them. Will applying my new attribute to the whole ViewModel class completely break client-side validation or will validation for the other properties be performed on the client and then total validation will be performed on the server?

Community
  • 1
  • 1
Maxim Zaslavsky
  • 17,787
  • 30
  • 107
  • 173
  • possible duplicate of [Class/Model Level Validation (as opposed to Property Level)? (ASP.NET MVC 2.0)](http://stackoverflow.com/questions/2783865/class-model-level-validation-as-opposed-to-property-level-asp-net-mvc-2-0) – John Farrell Jul 06 '10 at 00:13

1 Answers1

2

1) You can't access the outer class via a property level ValidationAttribute.

You could use a custom model binder to accomplish this. Simply detect the attributes and validate accordingly.

Creating a custom model binder: http://www.singingeels.com/Articles/Model_Binders_in_ASPNET_MVC.aspx

2) No. Did you try?

This is almost a duplicate. I'd check out the question and answers for my dupe submission. It may contain a separate technique, class level validation, that may do what you need.

John Farrell
  • 24,673
  • 10
  • 77
  • 110
  • You're right - I'm looking for a way to do class-level validation. I just relooked at the dupe you identified and I found your answer to that question. http://byatool.com/mvc/custom-data-annotations-with-mvc-how-to-check-multiple-properties-at-one-time/ is exactly what I want to do. That solves my first question, but I still don't know how to write to a property and whether this affects other client-side validation. – Maxim Zaslavsky Jul 06 '10 at 00:42
  • I have updated my question to clarify what I still don't understand after reading your answer. I think that should resolve the dupe issue, as the question you linked to isn't asking about writing to a property inside the class. – Maxim Zaslavsky Jul 06 '10 at 00:49
  • @Maxim Z, your code looks good and no it won't break client side validation. What don't you understand? – John Farrell Jul 06 '10 at 02:31