MapMvcAttributeRoutes: This method cannot be called during the application's pre-start initialization phase

asp.net-mvc, asp.net-mvc-5, attributerouting, unit-testing

Solution

I recently upgraded my project to ASP.NET MVC 5 and experienced the exact same issue. When using dotPeek to investigate it, I discovered that there is an internal `MapMvcAttributeRoutes` extension method that has a `IEnumerable<Type>` as a parameter which expects a list of controller types. I created a new extension method that uses reflection and allows me to test my attribute-based routes:

public static class RouteCollectionExtensions
{
    public static void MapMvcAttributeRoutesForTesting(this RouteCollection routes)
    {
        var controllers = (from t in typeof(HomeController).Assembly.GetExportedTypes()
                            where
                                t != null &&
                                t.IsPublic &&
                                t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
                                !t.IsAbstract &&
                                typeof(IController).IsAssignableFrom(t)
                            select t).ToList();

        var mapMvcAttributeRoutesMethod = typeof(RouteCollectionAttributeRoutingExtensions)
            .GetMethod(
                "MapMvcAttributeRoutes",
                BindingFlags.NonPublic | BindingFlags.Static,
                null,
                new Type[] { typeof(RouteCollection), typeof(IEnumerable<Type>) },
                null);

        mapMvcAttributeRoutesMethod.Invoke(null, new object[] { routes, controllers });
    }
}

And here is how I use it:

public class HomeControllerRouteTests
{
    [Fact]
    public void RequestTo_Root_ShouldMapTo_HomeIndex()
    {
        // Arrange
        var routes = new RouteCollection();

        // Act - registers traditional routes and the new attribute-defined routes
        RouteConfig.RegisterRoutes(routes);
        routes.MapMvcAttributeRoutesForTesting();

        // Assert - uses MvcRouteTester to test specific routes
        routes.ShouldMap("~/").To<HomeController>(x => x.Index());
    }
}

One problem now is that inside `RouteConfig.RegisterRoutes(route)` I cannot call `routes.MapMvcAttributeRoutes()` so I moved that call to my Global.asax file instead.

Another concern is that this solution is potentially fragile since the above method in `RouteCollectionAttributeRoutingExtensions` is internal and could be removed at any time. A proactive approach would be to check to see if the `mapMvcAttributeRoutesMethod` variable is null and provide an appropriate error/exceptionmessage if it is.

NOTE: This only works with ASP.NET MVC 5.0. There were significant changes to attribute routing in ASP.NET MVC 5.1 and the `mapMvcAttributeRoutesMethod` method was moved to an internal class.

Problem

I have a very simple test in a test project in a solution using ASP MVC V5 and attribute routing. Attribute routing and the `MapMvcAttributeRoutes` method are part of ASP MVC 5. ``` [Test] public void HasRoutesInTable() { var routes = new RouteCollection(); routes.MapMvcAttributeRoutes(); Assert.That(routes.Count, Is.GreaterThan(0)); } ``` This results in: ``` System.InvalidOperationException : This method cannot be called during the applications pre-start initialization phase. ``` Most of the answers to this error message involve configuring membership providers in the `web.config` file. This project has neither membership providers or a `web.config` file so the error seems be be occurring for some other reason. How do I move the code out of this "pre-start" state so that the tests can run? The equivalent code for attributes on `ApiController` works fine after `HttpConfiguration.EnsureInitialized()` is called.

Original source