I think you first have to understand that Autofac allows to register components with different lifetimes:
- Transient lifetime, via
InstancePerDependency
, which means the container will create a new instance every time it's asked to resolve a component
- Per lifetime scope, via
InstancePerLifetimeScope
, which means the container will resolve to the same instance of a component in a specific lifetime scope
- Singleton, via
SingleInstance
. In this case, at most one instance of the component is created by the container
So what does component disposal track mean?
It means that every lifetime scope keeps track of the components it owns. Upon disposal of the lifetime scope, every owned component that is disposable - i.e. that implements IDisposable
- will be disposed of.
So when will my components be disposed?
Going back to the first point, it depends which lifetime they've been registered with.
If the component is registered with the transient lifetime, all the instances will be disposed when the owning lifetime scope is disposed
If it's been registered as per-lifetime scope, then the one instance will be disposed when the owning lifetime scope is disposed
If the component has been registered as a singleton, the instance is owned by the root lifetime scope, and will only be disposed when that root lifetime scope is disposed
Some supporting code
public class TransientService : IDisposable
{
private static int _instanceCount = 0;
private readonly int _instanceNumber;
public TransientService()
{
_instanceCount++;
_instanceNumber = _instanceCount;
Console.WriteLine($"Just created TransientService #{_instanceNumber}");
}
public void Dispose()
{
Console.WriteLine($"Disposing TransientService #{_instanceNumber}");
}
}
public class LifetimeScopeService : IDisposable
{
private static int _instanceCount = 0;
private readonly int _instanceNumber;
public LifetimeScopeService()
{
_instanceCount++;
_instanceNumber = _instanceCount;
Console.WriteLine($"Just created LifetimeScopeService #{_instanceNumber}");
}
public void Dispose()
{
Console.WriteLine($"Disposing LifetimeScopeService #{_instanceNumber}");
}
}
public class SingletonService : IDisposable
{
private static int _instanceCount = 0;
private readonly int _instanceNumber;
public SingletonService()
{
_instanceCount++;
_instanceNumber = _instanceCount;
Console.WriteLine($"Just created SingletonService #{_instanceNumber}");
}
public void Dispose()
{
Console.WriteLine($"Disposing SingletonService #{_instanceNumber}");
}
}
class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder
.RegisterType<TransientService>()
.AsSelf()
.InstancePerDependency();
builder
.RegisterType<LifetimeScopeService>()
.AsSelf()
.InstancePerLifetimeScope();
builder
.RegisterType<SingletonService>()
.AsSelf()
.SingleInstance();
using (var container = builder.Build())
{
Console.WriteLine("Created the root scope");
var rootTransientService = container.Resolve<TransientService>();
var rootLifetimeScopeService = container.Resolve<LifetimeScopeService>();
var rootSingletonService = container.Resolve<SingletonService>();
var rootTransientServiceTwo = container.Resolve<TransientService>();
var rootLifetimeScopeServiceTwo = container.Resolve<LifetimeScopeService>();
var rootSingletonServiceTwo = container.Resolve<SingletonService>();
using (var outerLifetimeScope = container.BeginLifetimeScope())
{
Console.WriteLine("Created the outer lifetime scope");
var outerTransientService = outerLifetimeScope.Resolve<TransientService>();
var outerLifetimeScopeService = outerLifetimeScope.Resolve<LifetimeScopeService>();
var outerSingletonService = outerLifetimeScope.Resolve<SingletonService>();
var outerTransientServiceTwo = outerLifetimeScope.Resolve<TransientService>();
var outerLifetimeScopeServiceTwo = outerLifetimeScope.Resolve<LifetimeScopeService>();
var outerSingletonServiceTwo = outerLifetimeScope.Resolve<SingletonService>();
using (var innerLifetimeScope = container.BeginLifetimeScope())
{
Console.WriteLine("Created the inner lifetime scope");
var innerTransientService = innerLifetimeScope.Resolve<TransientService>();
var innerLifetimeScopeService = innerLifetimeScope.Resolve<LifetimeScopeService>();
var innerSingletonService = innerLifetimeScope.Resolve<SingletonService>();
var innerTransientServiceTwo = innerLifetimeScope.Resolve<TransientService>();
var innerLifetimeScopeServiceTwo = innerLifetimeScope.Resolve<LifetimeScopeService>();
var innerSingletonServiceTwo = innerLifetimeScope.Resolve<SingletonService>();
}
Console.WriteLine("Disposed the inner lifetime scope");
}
Console.WriteLine("Disposed the outer lifetime scope");
}
Console.WriteLine("Disposed the root scope");
Console.ReadLine();
}
}
3 services, one for each lifetime Autofac supports. The program is very simple, we have the root lifetime scope, an outer lifetime scope and an inner lifetime scope.
Each of these lifetime scopes resolve 2 instances of each service, so each service is resolved 6 times.
Here's the output:
Created the root scope
Just created TransientService #1
Just created LifetimeScopeService #1
Just created SingletonService #1
Just created TransientService #2
Created the outer lifetime scope
Just created TransientService #3
Just created LifetimeScopeService #2
Just created TransientService #4
Created the inner lifetime scope
Just created TransientService #5
Just created LifetimeScopeService #3
Just created TransientService #6
Disposing TransientService #6
Disposing LifetimeScopeService #3
Disposing TransientService #5
Disposed the inner lifetime scope
Disposing TransientService #4
Disposing LifetimeScopeService #2
Disposing TransientService #3
Disposed the outer lifetime scope
Disposing TransientService #2
Disposing SingletonService #1
Disposing LifetimeScopeService #1
Disposing TransientService #1
Disposed the root scope
A few observations, always keeping in mind that all services have been resolved 6 times by 3 different lifetime scopes
We ended up with 6 instances of the TransientService
, which matches the registration we made against the container. Disposal-wise, each lifetime scope disposed their 2 instances when themselves being disposed.
Only 3 instances of LifetimeScopeService
were created. While each lifetime scope resolved this service twice, Autofac always returned, within the same lifetime scope, the same instance the second time it was resolved. Each instance got disposed by the owning lifetime scope.
There has only been 1 instance of SingletonService
throughout the whole application. No lifetime scope boundary here since the 3 lifetime scopes resolved to the same instance of the service, which got disposed when the root scope was disposed.
Edit: indented the output to make it more readable and make the hierarchy clearer