C# 已处置带有OWIN TestServer和AutoFac-LifetimeScope的WebApi2

C# 已处置带有OWIN TestServer和AutoFac-LifetimeScope的WebApi2,c#,integration-testing,asp.net-web-api2,owin,autofac,C#,Integration Testing,Asp.net Web Api2,Owin,Autofac,使用Owin.TestServer测试应用程序时遇到问题。我找不到任何有用的东西,我希望这是一个社区可以帮助的简单修复:) 最近,我开始为使用OWIN和AutoFac for DI的WebApi应用程序编写集成测试。我总共有3个集成测试。当我单独运行每个测试时,它们都通过了。但是,当我同时运行所有测试时,由于以下AutoFac错误,只有第一个测试成功,其他两个测试失败: System.AggregateException: One or more errors occurred. --->

使用
Owin.TestServer
测试应用程序时遇到问题。我找不到任何有用的东西,我希望这是一个社区可以帮助的简单修复:)

最近,我开始为使用OWIN和AutoFac for DI的WebApi应用程序编写集成测试。我总共有3个集成测试。当我单独运行每个测试时,它们都通过了。但是,当我同时运行所有测试时,由于以下AutoFac错误,只有第一个测试成功,其他两个测试失败:

System.AggregateException: One or more errors occurred. ---> 
System.ObjectDisposedException: Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.
Stacktrace表示错误来自Owin的AutoFac中间件

我有以下测试设置:

[TestClass]
public class DinnerListControllerTests
{
    private TestServer _server;
    private TransactionScope _transactionScope;

    [TestInitialize]
    public void Init()
    {
        _transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew);
        _server = TestServer.Create<Startup>();
    }

    [TestCleanup]
    public void Dispose()
    {
        _server?.Dispose();
        _transactionScope?.Dispose();
    }

    [TestMethod]
    public void GetAllLists()
    {
        var response = _server.HttpClient.GetAsync("/api/dinnerlists").Result;
        response.IsSuccessStatusCode.Should().BeTrue("there should be no error");
        var result = response.Content.ReadAsAsync<IEnumerable<DinnerListDTO>>().Result;
        result.Should().NotBeNull().And.HaveCount(5);
    }

    [TestMethod]
    public void GetActiveListsReturnsTwoLists()
    {
        var response = _server.HttpClient.GetAsync("/api/dinnerlists/active").Result;
        response.IsSuccessStatusCode.Should().BeTrue();

        var result = response.Content.ReadAsAsync<IEnumerable<DinnerListDTO>>().Result;
        result.Should()
            .NotBeNullOrEmpty()
            .And.HaveCount(2)
            .And.OnlyContain(dto => dto.OpenUntil.CompareTo(DateTime.Now) > 0);
    }
}
Startup.cs:

public class Startup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        var httpConfiguration = new HttpConfiguration();

        WebApiConfig.Register(httpConfiguration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

        AutoFacConfig.ConfigureAutoFac(httpConfiguration);
        AutoMapperConfig.RegisterMappings();

        appBuilder.UseAutofacMiddleware(AutoFacConfig.Container);
        appBuilder.UseAutofacWebApi(httpConfiguration);
        appBuilder.UseWebApi(httpConfiguration);
    }
}
AutoFac模块示例:

public class RepositoryModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        var assembly = System.Reflection.Assembly.GetExecutingAssembly();
        builder.RegisterAssemblyTypes(assembly)
            .Where(type => type.Name.EndsWith("Repository"))
            .AsImplementedInterfaces()
            .InstancePerRequest();
    }
}

编辑-解决方案

@Eris的建议很有意义——我的
AutoFacConfig
类使用的是静态方法和成员,这意味着
Container
属性在后续测试中存在,不会再次创建,并且被标记为disposed

我决定重构代码,以便不再使用静态成员,因为我不想在应用程序关闭时处理容器

AutoFacConfig.cs:

public class AutoFacConfig
{
    private IContainer _container;
    public IContainer Container
    {
        get { return _container ?? (_container = BuildContainer()); }
    }

    public void ConfigureAutoFac(HttpConfiguration config)
    {
        //...
    }

    private IContainer BuildContainer()
    {
        var autoFacBuilder = new ContainerBuilder();
        //...
        return autoFacBuilder.Build();
    }
}
Startup.cs

public class Startup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        var httpConfiguration = new HttpConfiguration();
        var autofacConfig = new AutoFacConfig();  // create instance of AutoFacConfig           
        autofacConfig.ConfigureAutoFac(httpConfiguration); // configure autofac

        appBuilder.UseAutofacMiddleware(autofacConfig.Container);
        appBuilder.UseAutofacWebApi(httpConfiguration);
        appBuilder.UseWebApi(httpConfiguration);
    }
}

我怀疑是AutoFacConfig导致了一个问题:

    public static IContainer Container => _container ?? (_container = BuildContainer());
在这种情况下,
\u容器
不是空的,而是处于“Disposed”状态。如果您无条件地重新创建它,它应该可以工作。(我还不熟悉C#6语法,所以这可能不完全正确)

备选答复:

公共类启动
{
公共无效配置(IAppBuilder应用程序)
{
var context=新文本(app.Properties);
var token=context.Get(“host.OnAppDisposing”);
if(令牌!=CancellationToken.None)
{
令牌.寄存器(()=>
{
//要运行的代码
//消除可支配资源
});
}
}
}

如果您在一个测试中调用两个方法,会出现错误吗?是否有特定的原因让您为每个测试重新创建
\u server
\u transactionscope
,而不是为每个测试夹具、组件等创建一个服务器?@Eris-我认为这是一种方法,特别是使用事务范围,以便在测试之间保持数据库的一致性,因为其中一些测试操作数据库。我认为可能是这样,我将对此进行检查。然而,我怀疑处置测试服务器会处置整个应用程序和容器,但它实际上是一个单例,可能就是这样。我会在重构后返回结果。是的,处理静态项是一件棘手的事情,其内部内容超出了我目前的知识范围。工作非常出色-我会用解决方案更新问题谢谢,这为我在类似问题上指明了正确的方向
    public static IContainer Container => _container ?? (_container = BuildContainer());
    public static IContainer Container => _container = BuildContainer();
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var context = new OwinContext(app.Properties);
        var token = context.Get<CancellationToken>("host.OnAppDisposing");
        if (token != CancellationToken.None)
        {
            token.Register(() =>
            {
                // code to run 
                // null out disposable resources
            });
        }
    }
}