container.RegisterWebApiControllers(GlobalConfiguration.Configuration)导致InvalidOperationException

aba*_*hev 7 c# dependency-injection simple-injector asp.net-web-api

在我的集成测试中,我使用的是我正在测试的Web API项目中构建的相同的SimpleInjector.Container.

但是组合根类中的这一行:

container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
Run Code Online (Sandbox Code Playgroud)

导致异常:

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
Run Code Online (Sandbox Code Playgroud)

评论后,一切正常,网络应用程序本身和测试.

所以问题是:

  • 异常的原因是什么?
  • (这种方法真的需要吗?)

这是HttpClientFactory的代码(一个辅助类,用于创建具有适当头的HttpClient,例如api密钥或授权):

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

    public static HttpClient Create()
    {
        var client = new HttpClient { BaseAddress = GetUrl() };
        //...
        return client;
    }
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ven 6

如果我们仔细观察堆栈跟踪,我们可以准确地看到这里发生了什么.该RegisterWebApiControllers扩展方法调用GetControllerTypes的方法IHttpControllerTypeResolver其从抓住实例HttpConfiguration,并将其传递的IAssembliesResolver,其也从配置检索.被叫GetControllerTypes(方法WebHostHttpControllerTypeResolver)调用到GetControllerTypes了的DefaultHttpControllerTypeResolver,这将最终导致调用GetReferencedAssemblies了的System.Web.Compilation.BuildManager类.

System.Web.Compilation.BuildManager然而,不能在ASP.NET管道叫早,或ASP.NET的所有范围之外.由于您BuildManage正在进行测试,因此会抛出您遇到的异常.

因此,这里的解决方案(或"技巧")将IAssembliesResolver在单元测试时替换默认值.我认为旋转变压器看起来像这样:

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();
}
Run Code Online (Sandbox Code Playgroud)

你不得不处理这个问题有点不幸,特别是因为Simple Injector的设计是可测试的.我们似乎通过将RegisterWebApiControllers扩展方法与Web API深入集成来忽略这一点.我们必须退后一步,思考如何更轻松地验证单元测试中的Web API配置.