container.RegisterWebApiControllers(GlobalConfiguration.Configuration) causes InvalidOperationException

微笑、不失礼 提交于 2019-12-23 09:04:02

问题


In my integration tests I'm using the same SimpleInjector.Container which I construct in the Web API project I'm testing.

But this line in composition root class:

container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

causes an exception:

System.TypeInitializationException : The type initializer for 'MyProject.Api.Test.Integration.HttpClientFactory' threw an exception.
---- System.InvalidOperationException : This method cannot be called during the application's pre-start initialization phase.
Result StackTrace:  
at MyProject.Api.Test.Integration.HttpClientFactory.Create()
   at MyProject.Api.Test.Integration.Controllers.ProductControllerIntegrationTest.<GetProductBarcode_Should_Return_Status_BadRequest_When_Barcode_Is_Empty>d__0.MoveNext() in d:\Projects\My\MyProject.Api.Test.Integration\Controllers\ProductControllerIntegrationTest.cs:line 26
----- Inner Stack Trace -----
   at System.Web.Compilation.BuildManager.EnsureTopLevelFilesCompiled()
   at System.Web.Compilation.BuildManager.GetReferencedAssemblies()
   at System.Web.Http.WebHost.WebHostAssembliesResolver.System.Web.Http.Dispatcher.IAssembliesResolver.GetAssemblies()
   at System.Web.Http.Dispatcher.DefaultHttpControllerTypeResolver.GetControllerTypes(IAssembliesResolver assembliesResolver)
   at System.Web.Http.WebHost.WebHostHttpControllerTypeResolver.GetControllerTypes(IAssembliesResolver assembliesResolver)
   at SimpleInjector.SimpleInjectorWebApiExtensions.GetControllerTypesFromConfiguration(HttpConfiguration configuration)
   at SimpleInjector.SimpleInjectorWebApiExtensions.RegisterWebApiControllers(Container container, HttpConfiguration configuration)
   at MyProject.Api.ContainerConfig.RegisterTypes(Container container) in d:\Projects\My\MyProject.Api\App_Start\ContainerConfig.cs:line 128
   at MyProject.Api.ContainerConfig.CreateWebApiContainer() in d:\Projects\My\MyProject.Api\App_Start\ContainerConfig.cs:line 63
   at MyProject.Api.Test.Integration.HttpClientFactory..cctor() in d:\Projects\My\MyProject.Api.Test.Integration\HttpClientFactory.cs:line 17

After commenting it everything works fine, both the web app itself and the tests.

So the question is:

  • What is the reason for the exception?
  • (And is this method really required?)

Here's the code for HttpClientFactory (a helper class to create HttpClient with proper headers, such as api key or authorization):

internal static class HttpClientFactory
{
    private static readonly Container _container = ContainerConfig.CreateWebApiContainer();

    public static HttpClient Create()
    {
        var client = new HttpClient { BaseAddress = GetUrl() };
        //...
        return client;
    }
}

回答1:


If we look closely at the stack trace we can exactly see what is going on here. The RegisterWebApiControllers extension method calls the GetControllerTypes method on the IHttpControllerTypeResolver instance it grabs from the HttpConfiguration and it passes the IAssembliesResolver that is also retrieved from the configuration. The called GetControllerTypes method (of the WebHostHttpControllerTypeResolver) calls into the GetControllerTypes of the DefaultHttpControllerTypeResolver which will eventually cause a call to GetReferencedAssemblies of the System.Web.Compilation.BuildManager class.

The System.Web.Compilation.BuildManager however, can not be called early in the ASP.NET pipeline, or outside the context of ASP.NET at all. Since you're in a test, the BuildManage will throw the exception you are experiencing.

So the solution (or 'trick' so you will) here is to replace the default IAssembliesResolver when unit testing. I imagine that resolver to look like this:

public class TestAssembliesResolver : IAssembliesResolver
{
    public ICollection<Assembly> GetAssemblies()
    {
        return AppDomain.CurrentDomain.GetAssemblies();
    }
}

[TestMethod]
public void TestMethod1()
{
    // Replace the original IAssembliesResolver.
    GlobalConfiguration.Configuration.Services.Replace(typeof(IAssembliesResolver),
        new TestAssembliesResolver());

    var container = SimpleInjectorWebApiInitializer.BuildContainer();

    container.Verify();
}

It's a bit unfortunate that you have to deal this, especially since Simple Injector was designed to be testable. It seems that we overlooked this by integrating the RegisterWebApiControllers extension method so deeply with Web API. We have to take a step back and think about how to make it easier to verify the Web API configuration inside a unit test.




回答2:


A solution for SI v3.x is just ...

container.RegisterWebApiControllers(
  GlobalConfiguration.Configuration, 
  Assembly.GetExecutingAssembly()
);

.. so now it knows the assembly to search for controllers.



来源:https://stackoverflow.com/questions/26704486/container-registerwebapicontrollersglobalconfiguration-configuration-causes-in

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!