Project-wide controller survey through reflection


I often lose track of all of the different controllers in our system, especially if I’m trying to see what existing conventions we have in place for the design of actions.  To get around this, I use a simple LINQ query to display all of the controllers and actions in our system in an easily readable format:

var controllers =
    from t in GetAllControllerTypes()
    where typeof(Controller).IsAssignableFrom(t) && !t.IsAbstract
    orderby t.FullName
    from m in t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)
    where !m.IsSpecialName
    select new { ControllerName = FormatControllerName(t.FullName), ActionName = m.Name, Params = m.GetParameters() };

controllers.Each(c => Debug.WriteLine("Controller: " + c.ControllerName + ", Action: " + c.ActionName + "(" + string.Join(", ", c.Params.Select(p => p.Name).ToArray()) + ")"));
Debug.WriteLine("Controller/action count: " + controllers.Count());
Debug.WriteLine("Controller count: " + controllers.Distinct(c => c.ControllerName).Count());

The above LINQ query combines the controller name with each action, showing the action parameter names in a readable format.  The GetAllControllerTypes is just a method that returns all types in an assembly where my controllers can be found:

private static Type[] GetAllControllerTypes()
{
    return typeof(ProductController).Assembly.GetTypes();
}

Finally, I like to remove all of the namespace information from the controller names, but keep any potential area information (i.e., get rid of the root namespace, but keep any sub-namespaces:

private static string FormatControllerName(string typeName)
{
    return typeName.Replace("MvcApplication2.", string.Empty).Replace("Controllers.", string.Empty);
}

When I run this against a sample application provided by the MVC default project:

Controller: AccountController, Action: LogOn()
Controller: AccountController, Action: LogOn(userName, password, rememberMe, returnUrl)
Controller: AccountController, Action: LogOff()
Controller: AccountController, Action: Register()
Controller: AccountController, Action: Register(userName, email, password, confirmPassword)
Controller: AccountController, Action: ChangePassword()
Controller: AccountController, Action: ChangePassword(currentPassword, newPassword, confirmPassword)
Controller: AccountController, Action: ChangePasswordSuccess()
Controller: HomeController, Action: Index()
Controller: HomeController, Action: About()
Controller: ProductController, Action: Index()
Controller: ProductController, Action: Details(id)
Controller: ProductController, Action: Create()
Controller: ProductController, Action: Create(product)
Controller: ProductController, Action: Edit(id)
Controller: ProductController, Action: Edit(id, product)
Controller/action count: 16
Controller count: 3

The cool thing about LINQ against reflection is it lets you get these interesting surveys of your system.  When you start filtering it against base types, you can see a nice view of your system that things like ReSharper and other tools aren’t easily massaged.

Essential tools for web developers