Los Techies : Blogs about software and anything tech!

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.

Kick It on DotNetKicks.com
Posted May 14 2009, 10:28 PM by bogardj
Filed under: ,

Comments

Rob Scott wrote re: Project-wide controller survey through reflection
on 05-14-2009 11:08 PM

Nice.

Jeff Doolittle wrote re: Project-wide controller survey through reflection
on 05-15-2009 1:46 AM

Might be worth taking a look at NDepend.

Garry Shutler wrote re: Project-wide controller survey through reflection
on 05-15-2009 3:41 AM

I like it. What would be even cooler (haven't worked out in my head how you'd achieve it) would be if you could generate the URL for each of the actions as well.

Depaulo wrote re: Project-wide controller survey through reflection
on 05-15-2009 5:31 AM

Awesome! A problem we get sometimes is that when a domain class is made abstract, suddenly actions fail as the model binder can no longer instantiate the class. I have used your code here to create a test which runs through all our action method parameter types and try to instantiate them. Now we will never have the problem again! Hurrah!

bogardj wrote re: Project-wide controller survey through reflection
on 05-15-2009 8:24 AM

@Jeff

That's something I thought about...but since this code lives in an explicit unit test (my favorite sandbox), it's only a few keystrokes away inside VS.

@Depaulo

Oh yeah, we have another test that goes through our actions and finds "bad" ones.  You might be able to get that too with the Type.IsAbstract and doing another Where.

ASP.NET MVC Archived Blog Posts, Page 1 wrote ASP.NET MVC Archived Blog Posts, Page 1
on 05-18-2009 1:09 AM

Pingback from  ASP.NET MVC Archived Blog Posts, Page 1

DotNetShoutout wrote Project-wide controller survey through reflection - Jimmy Bogard - Los Techies
on 05-18-2009 8:56 PM

Thank you for submitting this cool story - Trackback from DotNetShoutout

drozzy wrote re: Project-wide controller survey through reflection
on 05-26-2009 11:22 AM

Just gota love the microsoft convention of naming obvious things in obvious ways:

Controller AccountController;

Why not go all the way and call a string for what it is? :-)

String first_name_string;

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems