在工作单元内获得的资源(数据库连接,事务,认证会话,文件句柄等)应在工作完成时销毁。 .NET提供了IDisposable
接口来帮助销毁这个更确定的概念。
一些IoC容器需要通过像ReleaseInstance()
这样的方法明确地告诉销毁特定的实例。 这使得很难保证使用正确的销毁语义。
- 从不可销毁部件切换到可销毁部件可能意味着修改客户端代码。
- 客户端代码在使用共享实例时可能会忽略销毁,但切换到非共享实例时几乎肯定无法清除。
Autofac使用生命周期作为销毁工作单元中创建的所有组件的一种方式来解决这些问题。
using (var scope = container.BeginLifetimeScope())
{
scope.Resolve<DisposableComponent>().DoSomething();
//在范围本身被销毁的情况下,在`'using'`声明的最后,销毁的范围组件被放置在这里。
}
生命周期的范围是在工作单元开始时创建的,当工作单元完成时,嵌套容器可以销毁超出范围的所有实例。
注册组件
Autofac可以自动销毁一些组件,但是您也可以手动指定销毁机制。
组件必须注册为InstancePerDependency()
(默认)或InstancePerLifetimeScope()
的一些变体(例如InstancePerMatchingLifetimeScope()
或InstancePerRequest()
)。
如果你有singleton组件(注册为SingleInstance()
),它们将在容器的整个生命周期中生存。由于容器的使用寿命通常是应用程序的生命周期,这意味着该组件在应用程序结束之前不会被销毁。
自动销毁
要利用自动确定性销毁,您的组件必须实现IDisposable
。然后,您可以根据需要注册组件,并在组件解析的每个生命周期作用域的末尾调用组件上的Dispose()
方法。
var builder = new ContainerBuilder();
builder.RegisterType<SomeDisposableComponent>();
var container = builder.Build();
//创建嵌套的生命周期作用域,解析组件并销毁作用域。
//您的组件将与范围一起被销毁。
指定销毁
如果你的组件没有实现IDisposable
,但是仍然需要在生命周期结束时进行一些清理,你可以使用OnRelease
生命周期事件。
var builder = new ContainerBuilder();
builder.RegisterType<SomeComponent>()
.OnRelease(instance => instance.CleanUp());
var container = builder.Build();
//创建嵌套的生命周期作用域,解析组件并销毁作用域。在处理作用域时,组件的“CleanUp()”方法将被调用。
请注意,OnRelease()
将覆盖IDisposable.Dispose()
的默认处理。 如果你的组件都实现了IDisposable并且需要一些其他的清理方法,你需要在OnRelease()
中手动调用Dispose()
,否则你需要更新你的类,以便从Dispose()
中调用清理方法。
禁用销毁
组件在默认情况下由容器拥有,并在适当的时候由容器销毁。 要禁用此功能,请将组件注册为拥有外部所有权:
builder.RegisterType<SomeComponent>().ExternallyOwned();
容器将永远不会对外部所有权注册的对象调用Dispose()
。处理以这种方式注册的组件是由你自己决定的。
禁用销毁的另一种方法是使用隐式关系Owned<T>
和拥有的实例。 在这种情况下,不是在消费代码中放置依赖项T
,而是依赖于Owned<T>
。 您的消费代码将负责处理。
public class Consumer
{
private Owned<DisposableComponent> _service;
public Consumer(Owned<DisposableComponent> service)
{
_service = service;
}
public void DoWork()
{
//_service用于某个任务
_service.Value.DoSomething();
//这里_service不再需要,所以它被释放
_service.Dispose();
}
}
您可以在拥有的实例主题中阅读有关Owned<T>
的更多信息。
从生命周期范围解析组件
生命周期范围是通过调用BeginLifetimeScope()
来创建的。 最简单的方法是在using
块。 使用生命周期作用域来解析组件,然后在工作单元完成时销毁作用域。
using (var lifetime = container.BeginLifetimeScope())
{
var component = lifetime.Resolve<SomeComponent>();
//组件及其任何可销毁依赖关系,将在using块完成时被销毁
}
请注意,使用Autofac集成库时,标准工作单元生命周期范围将自动创建并销毁。例如,在Autofac的ASP.NET MVC集成中,Web请求开始时会为您创建一个生命周期范围,并且所有组件都将从此处解析。在网络请求结束时,范围将自动销毁 - 您不需要额外创建范围。如果您正在使用其中一个集成库,则应该知道自动创建的范围可用的。
子作用域不自动处置
虽然生命周期范围本身实现了IDisposable
,但您创建的生命周期范围并不会自动销毁。如果您创建了一个生命周期范围,您需要负责调用Dispose()
来清理它并触发组件的自动处理。使用using
语句可以轻松完成此操作,但是如果您在不使用的情况下创建一个作用域,请不要忘记在完成后销毁该作用域。
区分您创建的范围和集成库为您创建的范围非常重要。您不必担心管理集成范围(如ASP.NET请求范围) - 这些将会为您完成。但是,如果您手动创建自己的范围,则将负责清理它。
高级层次结构
上面演示的最简单和最可取的资源管理场景是双层的:有一个单独的‘root’容器,每个工作单元都创建一个生命周期范围。可以创建更复杂的容器和组件的层次结构,但使用标记的生命周期范围。