Have you encountered the situation with your Episerver solution that your attribute based Web API starts throwing “The object has not yet been initialized. Ensure that HttpConfiguration.EnsureInitialized() is called in the application’s startup code after all other initialization code.”? If Yes, then this post is for you.
You have an Episerver solution and you have Web API endpoints created with attribute routing. Then suddenly the Web API endpoints start throwing exception stating “The object has not yet been initialized. Ensure that HttpConfiguration.EnsureInitialized() is called in the application’s startup code after all other initialization code.”. Stop and think, you didn’t change your code but you have added an Episerver Add-On maybe or some other NuGet package?
Simple sample to demonstrate this is to install Episerver Alloy MCV sample site with Episerver Find or Episerver Search. Then add some sample Web API endpoint and test it that it works. Next install the EPiServer.Marketing.Testing package (version 2.5.0) from Episerver NuGet feed. Rebuild your solution, run it and then try to call your Web API endpoint – it will now throw exception with the previously mentioned message.
Please note that the EPiServer.Marketing.Testing version 2.5.0 is old version used just to demo the issue. Currently at least the latest version 2.5.11 doesn’t cause this issue anymore.
So if you Google for Web API, Episerver and the exception message you will most likely end-up to one of the Episerver World forum threads like this or this. Which workaround the issue by using an Episerver initialization module that has dependency to FrameworkInitialization module and this “magically” fixes the issue. But wouldn’t it be important to understand why it fixes the issue after all the configuration is only moved to an earlier stage in Episerver initialization.
What causes the issue?
So as per the ASP.NET documentation the Web API 2 should be configured in the Application_Start method calling the GlobalConfiguration.Configure method and do the configuration. Now if you have an Add-On for example that wants to make sure its Web API attribute routing is registered it calls that same method or uses that method to register some other ‘classic’ routes using the MapHttRoute method. Now if you have correctly your code in Application_Start to register attribute routing it is the second time the GlobalConfiguration.Configure method is called and this second call will cause the issue.
GlobalConfiguration.Configure can be called multiple times BUT there is logic to run some code only once which leads to the fact that MapHttpAttributeRoutes() must be called only once before EnsureInitialized() is called. See the StackOverflow post that has good explanation what the method is about.
So back in 2.5.0 version of EPiServer.Marketing.Testing there is code that calls GlobalConfiguration.Configure with a delegate which actually only calls MapHttpRoute on the HttpConfiguration object and doesn’t call MapHttpAttributeRoutes() and in your code when you call the GlobalConfiguration.Configure and try to call MapHttpAttributeRoutes (this doesn’t throw) the EnsureInitialized is already called and leads to the situation that your attribute based routings will not be registered and exception is thrown when you try to call you Web API endpoints.
As a side note, if you call MapHttpRoute inside your Configure implementation also those routes are broken (unless you comment out the call to MapHttpAttributeRoutes()).
How to fix the issue?
First you could try to comment the call to MapHttpAttributeRoutes() in you GlobalConfiguration.Configure implementation – this is a long shot, if the ‘offending’ code is also calling MapHttpAttributeRoutes() everything should start to work again.
If that didn’t work then you need to implement an Episerver initialization module that runs before the code that ‘breakes’ the Web API configuration. Most modules take dependecy to EPiServer.Web.InitializationModule so your module most likely need to run before that to make the Web API configuration work. I have blogged and listed about the public Episerver initialization modules so have a look at that list (also if you are interested about the initialization framework look at the post by Waldis). So based on my listing your module should take dependency to EPiServer.Framework.FrameworkInitialization or EPiServer.Framework.FrameworkAspNetInitialization or anything else that is executed before the EPiServer.Web.InitializationModule. The Episerver World forum thread has the accepted answer which shows the initialization module you need to create and you can modify the ModuleDependency as needed.
Sample initialization module to fix the Web API attribute routing issue:
How to find the dependency in the offending module causing the issue you would need a .NET decompiler like ILSpy (or your some other favorite tool) and then browse the code to see what initialization module it has dependency to and to be able to precisly make your initialization module execute before the offending one. If you donät look at the offending code then you might need to try different initialization modules until you find an initizalition module that is executed before the offending modules initialization module dependency.
If you are building an Add-On that uses Web API attribute routing and therefore need to call the MapHttpAttributeRoutes() – then I think there are two options:
- call MapHttpAttributeRoutes() in you Add-ons initialization module but have an option to disable that with an AppSettings ‘key – value’ and document that information for your users
- don’t call MapHttpAttributeRoutes() but document to installation instructions that MapHttpAttributeRoutes() must be called in Application_Start like described in .NET Web API 2 configuration for your Add-On to work properly
If you are using the ‘old’ convention based routing then you can in your Add-Ons initialization module get a reference to the HttpConfiguration using the GlobalConfiguration.Configuration property and use that instance to add your convention based routes like: httpConfig.Routes.MapHttpRoute(your-code-here).
Sample initialization module to register routes in an Add-On:
Hopefully this post clarifys what is causing the issue and how it can be fixed. As a closing note, I would say that never call GlobalConfiguration.Configure method in your Add-ons, leave that to the application as it is its responsibility.