Search code examples
c#asp.net-core.net-coreinversion-of-control

Are objects from an implementation factory method disposed by the DI container .Net Core


When I register an implementation of an interface with an explicit implementationFactory, like this:

services.AddTransient<IDbConnection>(_ => new SqlConnection(connectionString));

The implementation is initialized when requested and disposed when out of scope by the DI container.

But will the SqlConnection also be disposed by the DI container when I do something like this:

services.AddTransient<IRepository>(_ => new Repository(new SqlConnection(connectionString)));

The intellisense isn't warning me about the undisposed object that implements IDisposable, but I don't think that's right.


Solution

  • No, container will not dispose in second case, because it does not know anything about this manually created instance:

    class MyDisposable : IDisposable
    {
        public bool Disposed { get; private set; }
        public void Dispose()
        {
            Disposed = true;
        }
    }
    
    class Container
    {
        public Container(MyDisposable d)
        {
            Disposable = d;
        }
        public MyDisposable Disposable { get; private set; }
    }
    
    var col = new ServiceCollection();
    col.AddTransient<MyDisposable>(_ => new MyDisposable());
    col.AddTransient<Container>(s => new Container(new MyDisposable()));
    
    Container container;
    MyDisposable disposable;
    using(var scope = col.BuildServiceProvider().CreateScope())
    {
        container = scope.ServiceProvider.GetRequiredService<Container>();
        disposable = scope.ServiceProvider.GetRequiredService<MyDisposable>();
    }
    Console.WriteLine(disposable.Disposed); // true
    Console.WriteLine(container.Disposable.Disposed); // false
    

    But if you change second registration to:

    col.AddTransient<Container>(s => new Container(s.GetRequiredService<MyDisposable>()));
    

    both MyDisposable's will be disposed.

    Using [Autofac][1], for example you can resolve current lifetime scope and schedule manually created instance for disposal when this scope ends:

    var builder = new ContainerBuilder();
    builder.Register(_ => new MyDisposable()).InstancePerDependency();
    builder.Register(ctx => 
    {
        var scope = ctx.Resolve<ILifetimeScope>();
        var dep = new MyDisposable();
        scope.Disposer.AddInstanceForDisposal(dep);
        return new Container(dep);
    })
    .InstancePerDependency();
    
    Container container;
    MyDisposable disposable;
    using(var scope = builder.Build().BeginLifetimeScope())
    {
        container = scope.Resolve<Container>();
        disposable = scope.Resolve<MyDisposable>();
    }
    
    Console.WriteLine(disposable.Disposed); // true
    Console.WriteLine(container.Disposable.Disposed);  // true