After reading Phil Haack’s blog post on Finding Bad Controllers I thought that it would be fun to reimplement using the compile time validation functionality of PostSharp. By moving this type of checking out of tests and making it part of the compilation chain we can ensure that our controller types are implemented correctly even if a developer forgets to add the tests to the test suite.
The code is pretty straightforward. we make the same checks outlined in Phil’s blog post: make sure the type name ends in “Controller”, that it implements IController and that it is a public, non-abstract, non-nested type.
All of the code used for this aspect is open source under an MIT license and is available in my Bitbucket repository. The code for the aspect is pretty straightforward:[gist: id=3320454]
We inherit from the TypeLevelAspect as we are going to perform compile time checking on types in our project.
First, we check and see if our type implements IController and it is public and non-abstract. If both of those are true but the type name does not end in “Controller” we will fail the build and inform the user that the type does not meet the required naming conventions.
Otherwise, if the type is correctly named and it is public and non-abstract but it does not implement IController we will also fail the build with a detailed error message.
Finally, if the type is correctly named and it implements IController but it is either non-public, nested or an abstract class we fail the build, again with an error message informing the developer what the problem is.
In order to report the filename and line number of the error we are again using either the experimental code analysis feature in PostSharp as demonstrated in my last post, or a version of CodeFreud (built on top of Project Roslyn). I updated the functionality of CodeFreud so now it can locate either methods or types in either a full solution or in a single project. Updating CodeFreud to work on a single project allows me to avoid the hack I used in the last post of storing the path to the solution file and passing it on to CodeFreud as I now can use the built in PostSharp property MSBuildProjectFullPath and send the current project to CodeFreud for analysis.
To test my aspect I put my aspect into Phil’s MvcHaack.ControllerInspector project where he had examples of bad controllers. In order to apply the compile time checking to the correct types I used a multicast attribute in the AssemblyInfo.cs file so that every type in the MvcHaack.ControllerInspector.DemoWeb.Controllers namespace will be verified.
[assembly: Aspects.VerifyController(AttributeTargetTypes = "MvcHaack.ControllerInspector.DemoWeb.Controllers.*")]
When I built the project I receive a nicely formatted list of build errors telling me exactly where my bad controllers are.
By using compile time validation I am able to provide quick and detailed feedback that will save myself and the other developers on my team from trying to diagnose why a particular controller is not working as expected. Since the actual build will fail this also prevents bad controller code from even being committed to my source control and lets me address problems where they are the cheapest to fix: on the developers workstation right as they are writing the code.
- Enhancing ASP.NET MVC Security with AOP
- Automatically Encrypting Properties With DPAPI