https://autofac.readthedocs.io/en/latest/lifetime/disposal.html
Resources obtained within a unit of work - database connections, transactions, authenticated sessions, file handles etc. - should be disposed of when that work is complete. .NET provides the IDisposable
interface to aid in this more deterministic notion of disposal.
Some IoC containers need to be told explicitly to dispose of a particular instance, through a method like ReleaseInstance()
. This makes it very difficult to guarantee that the correct disposal semantics are used.
- Switching implementations from a non-disposable to a disposable component can mean modifying client code.
- Client code that may have ignored disposal when using shared instances will almost certainly fail to clean up when switched to non-shared instances.
Autofac solves these problems using lifetime scopes as a way of disposing of all of the components created during a unit of work.
using (var scope = container.BeginLifetimeScope())
{
scope.Resolve<DisposableComponent>().DoSomething();
// Components for scope disposed here, at the
// end of the 'using' statement when the scope
// itself is disposed.
}
A lifetime scope is created when a unit of work begins, and when that unit of work is complete the nested container can dispose all of the instances within it that are out of scope.
Registering Components
Autofac can automatically dispose of some components, but you have the ability to manually specify a disposal mechanism, too.
Components must be registered as InstancePerDependency()
(the default) or some variation of InstancePerLifetimeScope()
(e.g., InstancePerMatchingLifetimeScope()
or InstancePerRequest()
).
If you have singleton components (registered as SingleInstance()
) they will live for the life of the container. Since container lifetimes are usually the application lifetime, it means the component won’t be disposed until the end of the application.
Automatic Disposal
To take advantage of automatic deterministic disposal, your component must implement IDisposable
. You can then register your component as needed and at the end of each lifetime scope in which the component is resolved, the Dispose()
method on the component will be called.
var builder = new ContainerBuilder(); builder.RegisterType<SomeDisposableComponent>(); var container = builder.Build(); // Create nested lifetime scopes, resolve // the component, and dispose of the scopes. // Your component will be disposed with the scope.
Asynchronous Disposal Support
If your components’ disposal behaviour requires some I/O activity, such as flushing a buffer to a file, or sending a packet over the network to close a connection, then you may want to consider implementing the new .NET IAsyncDisposable interface.
In Autofac 5.0, support was added for the IAsyncDisposable
interface, so lifetime scopes can now be disposed of asynchronously:
class MyComponent : IDisposable, IAsyncDisposable { INetworkResource myResource; public void Dispose() { myResource.Close(); } public async ValueTask DisposeAsync() { await myResource.CloseAsync(); } } // ... await using (var scope = container.BeginLifetimeScope()) { var service = scope.Resolve<MyComponent>(): // DisposeAsync will be called on MyComponent // when the using block exits. }
When a lifetime scope is disposed of asynchronously, any registered components that implement IAsyncDisposable
in addition to IDisposable
will have their DisposeAsync()
method invoked, instead of the Dispose()
method
If a component only implements the synchronous Dispose()
method, then it will still be invoked when the lifetime scope is disposed asynchronously.
When using Autofac with the ASP.NET Core Integration, all per-request lifetime scopes are disposed of asynchronously.
Important
While you do not have to implement
IDisposable
if you implementIAsyncDisposable
, we strongly recommend you do so.If your component only implements
IAsyncDisposable
, but someone disposes of the scope synchronously, then Autofac will throw an exception, because it does not know how to dispose of your component.
Specified Disposal
If your component doesn’t implement IDisposable
but still requires some cleanup at the end of a lifetime scope, you can use the OnRelease lifetime event.
var builder = new ContainerBuilder(); builder.RegisterType<SomeComponent>() .OnRelease(instance => instance.CleanUp()); var container = builder.Build(); // Create nested lifetime scopes, resolve // the component, and dispose of the scopes. // Your component's "CleanUp()" method will be // called when the scope is disposed.
Note that OnRelease()
overrides the default handling of IDisposable.Dispose()
. If your component both implements IDisposable
and requires some other cleanup method, you will either need to manually call Dispose()
in OnRelease()
or you will need to update your class so the cleanup method gets called from inside Dispose()
.