I just learned Autofac
and I have some problems to understand the Autofac lifetime scope. Please help review the code below.
using (var scope = container.BeginLifetimeScope())
{
// Resolve services from a scope that is a child
// of the root container.
var service = scope.Resolve<IService>();
// You can also create nested scopes...
using (var unitOfWorkScope = scope.BeginLifetimeScope())
{
var anotherService = unitOfWorkScope.Resolve<IOther>();
}
}
The documentation says: "Lifetime scopes are disposable and they track component disposal".
Does it mean the service
is disposable and can be recycled by the GC after the statement using (var scope = container.BeginLifetimeScope())
is finished?
And so does it to the anotherService
in the nested scope?
How can I test it?
Thanks.
InstancePerDependency
, which means the container will create a new instance every time it's asked to resolve a componentInstancePerLifetimeScope
, which means the container will resolve to the same instance of a component in a specific lifetime scopeSingleInstance
. In this case, at most one instance of the component is created by the containerIt 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.
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
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