Search code examples
c#dependency-injectionautofac

Autofac: how to prevent disposing service registered through ServiceCollection?


If I will register all services through ServiceCollection then I get capability for switching between different DI implementations that reused this collection: Autofac, Simple Injector, or Microsoft.Extensions.DependencyInjection.

But when I try to reuse the ServiceCollection through Autofac it disposes the external service. This is a problem...

Some external service:

namespace DI_Sandbox.Models
{

    public interface IMessageWriter
    {
        void Write(string message);
    }

    public class ConsoleMessageWriter: IMessageWriter, IDisposable
    {
        public void Dispose()
        {
            Console.WriteLine($"The {GetType().Name} instanse is disposed.");
        }

        public void Write(string message)
        {
            Console.WriteLine(message);
        }
    }
}

It is possible to prevent disposing service, registered in Autofac directly (i.e. without using ServiceCollection):

using Autofac;
using Autofac.Extensions.DependencyInjection;
using DI_Sandbox.Models;
using Microsoft.Extensions.DependencyInjection;

var externalService = new ConsoleMessageWriter();

var builder = new ContainerBuilder();
builder.RegisterInstance<IMessageWriter>(externalService).ExternallyOwned();
var container = builder.Build();

using(var serviceProvider = new AutofacServiceProvider(container))
{
    var messageWriter = serviceProvider.GetRequiredService<IMessageWriter>();
    messageWriter.Write("Hello DI!");
}

Console.Write("THE END");

The output is expected:

Hello DI!
THE END

My attempting to reuse in Autofac the services are registered through ServiceCollection:

using Autofac;
using Autofac.Extensions.DependencyInjection;
using DI_Sandbox.Models;
using Microsoft.Extensions.DependencyInjection;

var externalService = new ConsoleMessageWriter();

var services = new ServiceCollection();
services.AddSingleton<IMessageWriter>(externalService);

var builder = new ContainerBuilder();
builder.Populate(services);
var container = builder.Build();

using(var serviceProvider = new AutofacServiceProvider(container))
{
    var messageWriter = serviceProvider.GetRequiredService<IMessageWriter>();
    messageWriter.Write("Hello DI!");
}

Console.Write("THE END");

The result:

Hello DI!
The ConsoleMessageWriter instanse is disposed.
THE END

How to prevent disposing service is registered through ServiceCollection and reused by Autofac?


Solution

  • The problem you are running into is described in this issue:

    Apparently, the autofac adapter doesn't try to differentiate between singleton registrations when loading the service registrations from the IServiceCollection, and will end up not applying the ExternallyOwned flag or whatever to them.

    The author suggests the following workaround: register directly to Autofac: https://github.com/autofac/Autofac.Extensions.DependencyInjection/issues/15#issuecomment-314425353

    The workaround now is to register directly with Autofac instead of the adapter.

    Personally, I think this workaround is terrible and that the Autofac implementation is just buggy, but they went ahead and closed the issue. Apparently, from their perspective, it's working by design.

    I know this is a bit off topic, but I'd just honestly suggest dropping Autofac here and sticking with Microsoft.Extensions.DependencyInjection if you can.

    UPDATE April 29th 2024: