Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

asp.net mvc - how to enforce controller layer supertype

I'd like to use a class extending Controller as the default base type for controllers in my project, as opposed to using Controller itself. So, I would be doing this:

public class FooController : MyBaseController

Is there a way I can enforce this, so that people cannot create controllers which extend Controller directly?

like image 964
David Avatar asked Aug 08 '11 12:08

David


3 Answers

You can always create a unit test that (via reflection) retrieves all classes that derive from Controller and asserts that each class is also a subclass of MyBaseController. It would be along the lines of

[TestMethod]
public class All_Controllers_Derive_From_MyBaseController()
{
    // Act
    var controllerTypes = AppDomain.CurrentDomain
                                   .GetAssemblies()
                                   .SelectMany(asm => asm.GetTypes())
                                   .Where(t => t.IsSubclassOf(typeof(Controller))
                                   .ToList();

   // Verify
   foreach (var type in controllerTypes)
   {
        // Make sure the type isn't the actual controller type
        if (type is Controller)
            continue;

        Assert.IsTrue(type.IsSubclassOf(typeof(MyBaseController)), 
                           string.Format("{0} is not a subclass of the MyBaseController class", type.FullName));
   }
}

Now if someone creates a controller that doesn't use your base controller your unit tests will fail and tell you which ones are not correct.

Note that this code was written free-hand, so it might need some adjustment but that's the basic idea.

like image 106
KallDrexx Avatar answered Oct 21 '22 16:10

KallDrexx


However I prefer the unit testing approach above here is another one by using a custom controller factory.

public class MyControllerFactory<T> : DefaultControllerFactory where T : Controller
{
    #region Overrides of DefaultControllerFactory

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (!typeof(T).IsAssignableFrom(controllerType))
        {
            throw new NotSupportedException();
        }

        return base.GetControllerInstance(requestContext, controllerType);
    }

    #endregion
}

You can set it up in the application start method of your Global.asax like this:

ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory<MyBaseController>());

This of course causes a runtime exception when not deriving from MyBaseController which may not be suitable in your current scenario.

like image 3
gusztav.varga.dr Avatar answered Oct 21 '22 17:10

gusztav.varga.dr


You have two choices and both involve writing some code.

choice #1 You can create a filter and try and catch the error at runtime. You better have good a good user acceptance test process in place to touch all of the pages.

Choice #2 involves writing a task for MSBuild that checks that each controller class is derived from your specified class. Just load the application assembly (or assemblies) and go to town!

I prefer choice #2. It doesn't affect application runtime performance and gives you better coverage. You can run it at the end of a build.

http://msdn.microsoft.com/en-us/library/gg416513(VS.98).aspx

http://msdn.microsoft.com/en-us/library/t9883dzc.aspx

like image 1
Ken Brittain Avatar answered Oct 21 '22 16:10

Ken Brittain