autofac

Understanding the concept of Lifetime scope in Autofac


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.


Solution

  • I think you first have to understand that Autofac allows to register components with different lifetimes:

    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.

    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

    Edit: indented the output to make it more readable and make the hierarchy clearer