In the last post I mentioned how attributes were used to affect page functionality. The main class behind that is PageContext and its army of attributes: PageGroupAttribute, PageLayoutAttribute, and SecureAttribute.
The attributes control various aspects of the page without requiring the page author to clutter the implementation with virtual method overrides or explicit initialization code. For instance, the PageGroupAttribute (described below) could just as easily been a virtual property on a base page class however that would have meant requiring all site pages to derive from a common class. Not unheard of by any means but limits the possibilities in the future.
Here is a list of the attributes
The two classes related to this are PageContext and PageSettings. The former provides an entry point into the page functions and the latter handles looking at the attributes and returning data. I won't go into PageContext too much because in my next post I'm going to refactor it away.
The key to PageSettings is the CreateSettingsForType method (everything else is just accessors and a cache). This method takes the type passed to it and retrieves it custom attributes. For each attribute we see if it belongs to a type we care about and set the appropriate field.
The other thing we do is store the result in a Dictionary to use as a cache. This is safe because a class can't change its attributes (at least not without some dark magic) without recompilation (which will trigger an app reset) so whatever we store in the cache can be held without it going out of date.
Since we store every class for the application lifetime (once the page has been fetched) this dictionary has the potential to grow very large if you have a site with thousands of individual pages. If you have this problem and want to decrease the working-set of your application you can move the cache operations to the ASP.NET cache or you can use an alternate implementation*.
Note the empty catch at the end of the method. We are doing a double-check lock (the first check happens before the call to CreateSettingsForType()). Theoretically an update can sneak in before the lock but not be picked up by the second check. In this situation the add will throw the exception. The only downside is that we did a little extra work but it's only a one-time thing. We can safely ignore the exception. Usually eating an exception like this isn't a good idea but occasionally it is safe. Just make sure you put an appropriate amount of thought into your decision.
In my next post I will walk you through a simple refactoring of these classes.
* One alternate implementation would be to use interfaces. One interface for "page group" and "page layout" and a marker interface for "secure page". Page settings could take an IHttpHandler, cast it to each of the interfaces, and an appropriately constructed PageSettings object without storing it in the cache.