Implementing a class-level validation attribute that writes to a property inside the class
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 res开发者_如何学运维ults 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?
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.
精彩评论