A small gotcha with PageTypeBuilder-classes and reflecting attributes

I was developing functionality where I needed to alter certain properties when a page is published. Since the properties were pretty arbitrary (as in different property names spread out on different page types) the properties were marked with an attribute. The event that handles the PublishingPage event would then simply check the page being published and perform some action on all properties that are marked. The properties in question would be fetched from a class that looks something like this:

public class FindPropertiesToGenerateThreadIdFor : IFindPropertiesToGenerateThreadIdFor
{
    public IEnumerable<PropertyInfo> FindProperties(TypedPageData page)
    {
        return page
            .GetType()
            .GetProperties()
            .Where(m => m.GetCustomAttributes(typeof (GenerateThreadIdForReviewAttribute), true).Count() > 0);
    }
}

Given that the property OverviewBody in the ArticlePage is marked with the attribute GenerateThreadIdForReview the following test (in psuedo code here) will pass

private IEnumerable<PropertyInfo> _properties;

protected override void given()
{
    ArticlePage = new ArticlePage();
}

protected override void when()
{
    _properties = SystemUnderTest.FindProperties(ArticlePage);
}

[Test]
public void should_find_all_properties_that_are_marked_with_GenerateThreadIdForReviewAttribute()
{
    Assert.That(_properties.Count(), Is.EqualTo(1));
}

Reflecting attributes and dynamic proxy

Since the test passed I was slightly surprised when I noticed the action was not performed when the page was published. So, as a last resort I started to debug and it became quite apparent were the problem was. Since PageTypeBuilder uses dynamic proxy the page is not actually of the type ArticlePage but rather a proxy of it. The implementation of GetCustomAttributes on PropertyInfo (which it inherits from MemberInfo) doesn’t take the inheritance chain into account when looking for attributes on the properties. This basically means it won’t find the attribute since it’s not defined on the proxy class. More information on this can be found in this post.

The fix

Luckily, This is quite easily fixed by using the static method on the Attribute class directly.

return page
    .GetType()
    .GetProperties()
    .Where(m => Attribute.GetCustomAttributes(m, typeof(GenerateThreadIdForReviewAttribute), true).Count() > 0);