1

Closed

WebApi Exception in deployed project not using WebApi

description

The project works fine in Visual Studio but when I load it onto an IIS 8.0 Windows 2012 web server, I get the following 'yellow screen of death':

[MissingMethodException: Method not found: 'System.Web.Http.Controllers.ServicesContainer System.Web.Http.HttpConfiguration.get_Services()'.]
Navigation.WebApiRouteConfig..cctor() +0

[TypeInitializationException: The type initializer for 'Navigation.WebApiRouteConfig' threw an exception.]
Navigation.WebApiRouteConfig.AddHttpRoute(State state) +0
Navigation.StateInfoConfig.AddStateRoutes() +805

[InvalidOperationException: The pre-application start initialization method AddStateRoutes on type Navigation.StateInfoConfig threw an exception with the following error message: The type initializer for 'Navigation.WebApiRouteConfig' threw an exception..]
System.Web.Compilation.BuildManager.InvokePreStartInitMethodsCore(ICollection1 methods, Func1 setHostingEnvironmentCultures) +12969195
System.Web.Compilation.BuildManager.InvokePreStartInitMethods(ICollection`1 methods) +12968904
System.Web.Compilation.BuildManager.CallPreStartInitMethods(String preStartInitListPath, Boolean& isRefAssemblyLoaded) +280
System.Web.Compilation.BuildManager.ExecutePreAppStart() +172
System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel, Exception appDomainCreationException) +1151

[HttpException (0x80004005): The pre-application start initialization method AddStateRoutes on type Navigation.StateInfoConfig threw an exception with the following error message: The type initializer for 'Navigation.WebApiRouteConfig' threw an exception..]
System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +12968244
System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +159
System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +12807949

A quick Google suggested that this was down to IIS using an older version of System.Web.Http, but I don't think this is the case.

The IIS box is fully patched and is happily running other ASP.net 4.5 Web Forms using Owin and ASP.net Identity. I suspect the problem is a local IIS config issue, but I thought I would ask if you had seen it before?
Closed Jul 21, 2014 at 5:45 PM by GrahamMendick
Thanks Nielski.
This fix works is because of the way the C# JIT works. It only compiles a function just before it runs it. The Initialise function is never run if there's no WebApi States and so there's no dependency on WebApi. Moving the logic from the Initialise method into the AddHttpRoute method would introduce a WebApi dependency because it would then compile it as part of the AddHttpRoute method.

Note, if you're using Ngen to compile ahead of time instead of JIT then you'll need to include all the packages, in particular WebApi.

comments

GrahamMendick wrote Jul 19, 2014 at 8:14 PM

A temporary workaround is to include a reference to the WebApi package.
Investigation is underway for a permanent fix.

Neilski wrote Jul 20, 2014 at 3:37 PM

After much support from the author (thank you Graham - absolutely brilliant), the following fix has been developed.

In both the MVC RouteConfig and the WebApi WebApiRouteConfig classes, the ConfigurationService is called in the static class's constructor method. It is this workflow that requires that the MVC/WebApi reference libraries be available, even if they are not being used.

The fix is to move the ConfigurationService initialisation out of the constructors and add it to the AddRoute()/AddHttpRoute() methods only if a MVC/WebApi reference route exists. A simple boolean flag is used to ensure that the initialisation is only called once.

RouteConfig.cs
#if NET40Plus
using System.Web.Mvc;
using System.Web.Routing;

namespace Navigation
{
    internal static class RouteConfig
    {

        private static bool _Initialised = false;

        internal static void AddRoute(State state)
        {
            string controller = state.Attributes["controller"] != null ? state.Attributes["controller"].Trim() : string.Empty;
            string action = state.Attributes["action"] != null ? state.Attributes["action"].Trim() : string.Empty;
            if (controller.Length != 0 && action.Length != 0)
            {
                if (!_Initialised)
                {
                    Initialise();
                    _Initialised = true;
                }
                string area = state.Attributes["area"] != null ? state.Attributes["area"].Trim() : string.Empty;
                state.StateHandler = new MvcStateHandler();
                Route route = RouteTable.Routes.MapRoute("Mvc" + state.Id, state.Route);
                route.Defaults = StateInfoConfig.GetRouteDefaults(state, state.Route);
                route.Defaults["controller"] = controller;
                route.Defaults["action"] = action;
                route.DataTokens = new RouteValueDictionary() { { NavigationSettings.Config.StateIdKey, state.Id } };
                if (area.Length != 0)
                    route.DataTokens["area"] = area;
                route.RouteHandler = new MvcStateRouteHandler(state);
            }
        }

        private static void Initialise()
        {
            ValueProviderFactories.Factories.Insert(3, new NavigationDataMvcValueProviderFactory());
            GlobalFilters.Filters.Add(new RefreshAjaxAttribute());
        }
    }
}
#endif
WebApiRouteConfig.cs
#if NET40Plus
using System.Web.Http;
using System.Web.Http.ValueProviders;
using System.Web.Routing;

namespace Navigation
{
    internal static class WebApiRouteConfig
    {

        private static bool _Initialised = false;

        internal static void AddHttpRoute(State state)
        {
            string apiController = state.Attributes["apiController"] != null ? state.Attributes["apiController"].Trim() : string.Empty;
            string action = state.Attributes["action"] != null ? state.Attributes["action"].Trim() : string.Empty;
            if (apiController.Length != 0 && action.Length != 0)
            {
                if (!_Initialised)
                {
                    Initialise();
                    _Initialised = true;
                }
                state.StateHandler = new WebApiStateHandler();
                RouteValueDictionary defaults = StateInfoConfig.GetRouteDefaults(state, state.Route);
                defaults["controller"] = apiController;
                defaults["action"] = action;
                GlobalConfiguration.Configuration.Routes.MapHttpRoute("WebApi" + state.Id, state.Route, defaults);
                Route route = (Route)RouteTable.Routes["WebApi" + state.Id];
                route.DataTokens = new RouteValueDictionary() { { NavigationSettings.Config.StateIdKey, state.Id } };
                route.RouteHandler = new WebApiStateRouteHandler(state);
            }
        }

        private static void Initialise()
        {
            GlobalConfiguration.Configuration.Services
                .Insert(typeof(ValueProviderFactory), 0, new NavigationDataValueWebApiProviderFactory());
        }
    }
}
#endif