如何使用 Autofac 解决 Nancy 创建的子生命周期范围内类型的每个请求依赖项的实例
How to use Autofac to resolve instance per request dependencies for types in a child lifetime scope created by Nancy
我们有几个应用程序托管在 Windows 服务中,这些服务自行托管 Nancy 端点,以便公开有关应用程序操作的检测。
我们使用 Autofac 作为我们的 IOC。多个存储库注册到所有应用程序共享的核心 DLL 中的根容器中;然后使用从 Nancy.Autofac.Bootstrapper
.
派生的引导程序将该容器作为其容器传递给 Nancy
我们发现,当 Nancy 收到 Web 请求时,它会解析来自根容器的存储库请求,这导致内存被非垃圾收集器消耗 IDisposable
s 作为根容器不会超出范围(它具有 windows 服务的生命周期)。这导致了服务 "leaking" 内存。
然后我们切换到一个模型,在该模型中我们使用 InstancePerRequest
在 Nancy 引导程序中重写的 ConfigureRequestContainer()
方法中添加了存储库注册:
protected override void ConfigureRequestContainer(ILifetimeScope container, NancyContext context)
{
base.ConfigureRequestContainer(container, context);
PerRequestContainerBuilder().Update(container.ComponentRegistry);
}
private static ContainerBuilder PerRequestContainerBuilder()
{
var builder = new ContainerBuilder();
// Dependency for repository
builder.RegisterType<SystemDateTimeProvider>().InstancePerRequest().As<IDateTimeProvider>();
// Repository
builder.RegisterType<BookmarkRepository>().InstancePerRequest().As<IBookmarkRepository>();
return builder;
}
我们还覆盖了 CreateRequestContainer()
方法来创建带有标签 MatchingScopeLifetimeTags.RequestLifetimeScopeTag
的请求容器。
protected override ILifetimeScope CreateRequestContainer(NancyContext context)
{
return ApplicationContainer.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
}
这似乎已经解决了 IDisposable
未被处理的问题 - 子请求容器被处理在 Web 请求管道的末尾,由它解析的对象也被处理并最终被垃圾收集。
我们的问题是,这似乎将存储库的实现细节泄露到服务中,因为我们不仅要在 ConfigureRequestContainer()
中注册存储库,还要注册存储库所需的任何其他对象,即如果我们想更改存储库的实现,我们必须 "walk the dependency chain" 在每个使用它的服务中注册所需的对象 - 这似乎是错误的。
有没有办法让 Autofac 从根容器中解析存储库的支持对象,但将注册信息保留在 Web 请求容器的范围内?或者有没有办法在创建子容器时自动将现有注册从根容器复制到子容器中?
Autofac 应该自动解析来自 "parent" 个生命周期的实例。如果您使用 InstancePerRequest
配置注册,Autofac 将使用特殊的生命周期标签 MatchingScopeLifetimeTags.RequestLifetimeScopeTag
注册这些服务,以便稍后在正确的范围内解析。
这意味着无需使用 Nancy 引导程序的 ConfigureRequestContainer
方法来进行请求范围内的注册。你已经做到了!只要 Nancy 使用 InstancePerRequest
中使用的相同标记创建请求生命周期(从 Nancy 1.1 开始,这是 done by default),服务就应该正确解析。
示例:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// Do request-scoped registrations using InstancePerRequest...
var container = builder.Build();
// Pass the pre-built container to the bootstrapper
var bootstrapper = new MyAwesomeNancyBootstrapper(container);
app.UseNancy(options => options.Bootstrapper = bootstrapper);
}
}
public class MyAwesomeNancyBootstrapper : AutofacNancyBootstrapper
{
private readonly ILifetimeScope _lifetimeScope;
public MyAwesomeNancyBootstrapper(ILifetimeScope lifetimeScope)
{
_lifetimeScope = lifetimeScope;
}
protected override ILifetimeScope GetApplicationContainer()
{
return _lifetimeScope; // Tell Nancy you've got a container ready to go ;)
}
}
这个设置应该足够了(从 Nancy 1.1 开始。在早期版本中,您还必须重写 CreateRequestContainer
方法并在创建请求生命周期范围时传递请求生命周期标记)。
为您整理了一个示例
我们有几个应用程序托管在 Windows 服务中,这些服务自行托管 Nancy 端点,以便公开有关应用程序操作的检测。
我们使用 Autofac 作为我们的 IOC。多个存储库注册到所有应用程序共享的核心 DLL 中的根容器中;然后使用从 Nancy.Autofac.Bootstrapper
.
我们发现,当 Nancy 收到 Web 请求时,它会解析来自根容器的存储库请求,这导致内存被非垃圾收集器消耗 IDisposable
s 作为根容器不会超出范围(它具有 windows 服务的生命周期)。这导致了服务 "leaking" 内存。
然后我们切换到一个模型,在该模型中我们使用 InstancePerRequest
在 Nancy 引导程序中重写的 ConfigureRequestContainer()
方法中添加了存储库注册:
protected override void ConfigureRequestContainer(ILifetimeScope container, NancyContext context)
{
base.ConfigureRequestContainer(container, context);
PerRequestContainerBuilder().Update(container.ComponentRegistry);
}
private static ContainerBuilder PerRequestContainerBuilder()
{
var builder = new ContainerBuilder();
// Dependency for repository
builder.RegisterType<SystemDateTimeProvider>().InstancePerRequest().As<IDateTimeProvider>();
// Repository
builder.RegisterType<BookmarkRepository>().InstancePerRequest().As<IBookmarkRepository>();
return builder;
}
我们还覆盖了 CreateRequestContainer()
方法来创建带有标签 MatchingScopeLifetimeTags.RequestLifetimeScopeTag
的请求容器。
protected override ILifetimeScope CreateRequestContainer(NancyContext context)
{
return ApplicationContainer.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
}
这似乎已经解决了 IDisposable
未被处理的问题 - 子请求容器被处理在 Web 请求管道的末尾,由它解析的对象也被处理并最终被垃圾收集。
我们的问题是,这似乎将存储库的实现细节泄露到服务中,因为我们不仅要在 ConfigureRequestContainer()
中注册存储库,还要注册存储库所需的任何其他对象,即如果我们想更改存储库的实现,我们必须 "walk the dependency chain" 在每个使用它的服务中注册所需的对象 - 这似乎是错误的。
有没有办法让 Autofac 从根容器中解析存储库的支持对象,但将注册信息保留在 Web 请求容器的范围内?或者有没有办法在创建子容器时自动将现有注册从根容器复制到子容器中?
Autofac 应该自动解析来自 "parent" 个生命周期的实例。如果您使用 InstancePerRequest
配置注册,Autofac 将使用特殊的生命周期标签 MatchingScopeLifetimeTags.RequestLifetimeScopeTag
注册这些服务,以便稍后在正确的范围内解析。
这意味着无需使用 Nancy 引导程序的 ConfigureRequestContainer
方法来进行请求范围内的注册。你已经做到了!只要 Nancy 使用 InstancePerRequest
中使用的相同标记创建请求生命周期(从 Nancy 1.1 开始,这是 done by default),服务就应该正确解析。
示例:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// Do request-scoped registrations using InstancePerRequest...
var container = builder.Build();
// Pass the pre-built container to the bootstrapper
var bootstrapper = new MyAwesomeNancyBootstrapper(container);
app.UseNancy(options => options.Bootstrapper = bootstrapper);
}
}
public class MyAwesomeNancyBootstrapper : AutofacNancyBootstrapper
{
private readonly ILifetimeScope _lifetimeScope;
public MyAwesomeNancyBootstrapper(ILifetimeScope lifetimeScope)
{
_lifetimeScope = lifetimeScope;
}
protected override ILifetimeScope GetApplicationContainer()
{
return _lifetimeScope; // Tell Nancy you've got a container ready to go ;)
}
}
这个设置应该足够了(从 Nancy 1.1 开始。在早期版本中,您还必须重写 CreateRequestContainer
方法并在创建请求生命周期范围时传递请求生命周期标记)。