带有 OWIN TestServer 和 AutoFac 的 WebApi2 - LifetimeScope 已经配置
WebApi2 with OWIN TestServer and AutoFac - LifetimeScope already disposed
我在使用 Owin.TestServer
测试我的应用程序时遇到了问题。我找不到任何有用的东西,我希望这是社区可以提供帮助的简单修复:)
最近我开始为使用 OWIN 和 AutoFac 进行 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);
}
}
GetAllLists
测试将正确执行,但第二个测试将失败并显示上述消息。
我试过依赖的不同注册范围,但没有用。下面是我的 AutoFac 配置、启动 class 和示例 AutoFac 模块:
AutoFacConfig.cs
public class AutoFacConfig
{
private static IContainer _container;
public static IContainer Container => _container ?? (_container = BuildContainer());
public static void ConfigureAutoFac(HttpConfiguration config)
{
if (config == null)
throw new ArgumentNullException(nameof(config));
FluentValidationModelValidatorProvider.Configure(config,
provider => provider.ValidatorFactory = new AutoFacValidatorFactory(Container));
config.DependencyResolver = new AutofacWebApiDependencyResolver(Container);
}
private static IContainer BuildContainer()
{
var autoFacBuilder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
autoFacBuilder.RegisterApiControllers(assembly).InstancePerRequest();
autoFacBuilder.RegisterType<DinnerDbContext>().InstancePerRequest();
autoFacBuilder.RegisterModule<RepositoryModule>();
autoFacBuilder.RegisterModule<ServicesModule>();
autoFacBuilder.RegisterModule<ValidationModule>();
autoFacBuilder.RegisterModule<AutoMapperModule>();
autoFacBuilder.RegisterModule<AutofacWebTypesModule>();
return autoFacBuilder.Build();
}
}
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
class 使用静态方法和成员,这意味着 Container
属性 出现在随后的测试,它没有被再次创建,它被标记为已处置。
我决定重构代码,因此 AutoFacConfig
不再使用静态成员,因为我不想在应用程序关闭时处理容器。
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());
在这种情况下,_container
不是 null,而是处于 "Disposed" 状态。如果您无条件地重新创建它,它应该可以工作。 (我还不熟悉 C#6 语法,所以这可能不完全正确)
public static IContainer Container => _container = BuildContainer();
备选答案:
In self-hosted OWIN Web API, how to run code at shutdown?
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
});
}
}
}
我在使用 Owin.TestServer
测试我的应用程序时遇到了问题。我找不到任何有用的东西,我希望这是社区可以提供帮助的简单修复:)
最近我开始为使用 OWIN 和 AutoFac 进行 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);
}
}
GetAllLists
测试将正确执行,但第二个测试将失败并显示上述消息。
我试过依赖的不同注册范围,但没有用。下面是我的 AutoFac 配置、启动 class 和示例 AutoFac 模块:
AutoFacConfig.cs
public class AutoFacConfig
{
private static IContainer _container;
public static IContainer Container => _container ?? (_container = BuildContainer());
public static void ConfigureAutoFac(HttpConfiguration config)
{
if (config == null)
throw new ArgumentNullException(nameof(config));
FluentValidationModelValidatorProvider.Configure(config,
provider => provider.ValidatorFactory = new AutoFacValidatorFactory(Container));
config.DependencyResolver = new AutofacWebApiDependencyResolver(Container);
}
private static IContainer BuildContainer()
{
var autoFacBuilder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
autoFacBuilder.RegisterApiControllers(assembly).InstancePerRequest();
autoFacBuilder.RegisterType<DinnerDbContext>().InstancePerRequest();
autoFacBuilder.RegisterModule<RepositoryModule>();
autoFacBuilder.RegisterModule<ServicesModule>();
autoFacBuilder.RegisterModule<ValidationModule>();
autoFacBuilder.RegisterModule<AutoMapperModule>();
autoFacBuilder.RegisterModule<AutofacWebTypesModule>();
return autoFacBuilder.Build();
}
}
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
class 使用静态方法和成员,这意味着 Container
属性 出现在随后的测试,它没有被再次创建,它被标记为已处置。
我决定重构代码,因此 AutoFacConfig
不再使用静态成员,因为我不想在应用程序关闭时处理容器。
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());
在这种情况下,_container
不是 null,而是处于 "Disposed" 状态。如果您无条件地重新创建它,它应该可以工作。 (我还不熟悉 C#6 语法,所以这可能不完全正确)
public static IContainer Container => _container = BuildContainer();
备选答案: In self-hosted OWIN Web API, how to run code at shutdown?
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
});
}
}
}