views:

166

answers:

2

I'm trying to query attributes and access the base type of several attributes. For some reason it is failing to cast properly and not executing as expected. Here is the code below:

    internal static void ValidateProperties(TModel model, ModelStateDictionary modelState)
    {
        Type type = model.GetType();            
        PropertyInfo[] properties = type.GetProperties();
        foreach (PropertyInfo property in properties)
        {                
            foreach(Attribute attribute in property.GetCustomAttributes(true))
            {
                ValidationAttribute validationAttribute = attribute as ValidationAttribute;
                if (validationAttribute != null)
                {                        
                    if (!validationAttribute.IsValid(property.GetValue(model, null)))
                        modelState.AddModelError(property.Name, validationAttribute.ErrorMessage);
                }
            }
        }            
    } 

For some reason validateAttribute is always null. In debugging it won't cast CustomValidationAttribute or RequiredAttribute. Trying to directly cast to ValidationAttribute instead of using the "as" keyword results in an Invalid Cast exception for both of these types as well. Clearly they are derived from ValidationAttribute: http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.validationattribute.aspx

What am I missing? Thanks!


Edit: Per request, I've added an Debug statement to output the Type of the Attribute. The results are as follows:

System.ComponentModel.DataAnnotations.CustomValidationAttribute System.ComponentModel.DataAnnotations.RequiredAttribute System.ComponentModel.DataAnnotations.RegularExpressionAttribute System.ComponentModel.DataAnnotations.CustomValidationAttribute System.ComponentModel.DataAnnotations.RequiredAttribute

A: 

EDIT:

Before actually casting please do type-check for Attribute.

Instead of directly casting..

ValidationAttribute validationAttribute = attribute as ValidationAttribute;

Check..

if(!(attribute is ValidationAttribute))
{
    continue;
}
ValidationAttribute validationAttribute = attribute as ValidationAttribute;

Original:

This is how you can do this.

Why iterate through all attributes of a property, instead filter all properties having attribute of type ValidationAttribute.

        PropertyInfo[] properties = model.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

        var valProps = from PropertyInfo property in properties
                       where property.GetCustomAttributes(typeof(ValidationAttribute), true).Length > 0
                       select new
                       {
                           Property = property,
                           ValidationAttributes = property.GetCustomAttributes(typeof(ValidationAttribute), true)
                       };

        foreach (var item in valProps)
        {
            foreach (ValidationAttribute attribute in item.ValidationAttributes)
            {
                if (attribute.IsValid(item.Property.GetValue(model, null)))
                {
                    continue;
                }

                //Validation fails..
            }
        }
    }
this. __curious_geek
That was my original technique. It was not finding any properties that had a ValidationAttribute. I changed it to the code above to diagnose exactly where the error was occurring. Regardless, the code you posted does not work either, valProps contains 0 records.
bmancini
Please check updated answer.
this. __curious_geek
+2  A: 

It doesn't look like you're missing anything or doing anything wrong, so here's a shot in the dark: are the assemblies (the one querying and the one being queried) using different (strong-named) versions of the System.ComponentModel.DataAnnotations.dll?

Jeff Sternal
That's a good guess. The function above is in a different assembly than the ones being passed into it. I'll have to check on this when I get home today.
bmancini
Ding ding ding! Nailed it. I forgot that the MVC app was using a the MVC DataAnnotations version of System.ComopondentModel.DataAnnotations. In the other assembly I referenced from the .NET Framework. Problem resolved when I used the MVC DataAnnotations version in the other assembly. Thanks!
bmancini