Issue with disposable singletons
See original GitHub issueLet’s assume we have the following types:
public interface IMyInterface
{
}
public sealed class MyClass : IMyInterface, IDisposable
{
public void Dispose()
{
Console.WriteLine("Disposing");
}
}
When using Microsoft ASP.NET DependencyInjection the following code works correctly:
private static void MicrosoftDI_1()
{
var sc = new ServiceCollection();
var externallyOwned = new MyClass();
sc.AddSingleton<IMyInterface>(externallyOwned);
var sp = sc.BuildServiceProvider();
var myObj = sp.GetRequiredService<IMyInterface>();
((IDisposable)sp).Dispose(); // myObj is _not_ disposed
}
The externally owned singleton is not disposed - that’s correct.
The following code works correctly, too:
private static void MicrosoftDI_2()
{
var sc = new ServiceCollection();
sc.AddSingleton<IMyInterface>(_ => new MyClass());
var sp = sc.BuildServiceProvider();
var myObj = sp.GetRequiredService<IMyInterface>();
((IDisposable)sp).Dispose(); // myObj is disposed
}
Here the singleton myObj
is disposed, because it is created inside of a lamdba factory function -
correct so far.
Now let’s see what Autofac using the Microsoft ASP.NET DependencyInjection adapter is doing:
private static void AutofacDI_1()
{
var sc = new ServiceCollection();
var externallyOwned = new MyClass();
sc.AddSingleton<IMyInterface>(externallyOwned);
var cb = new ContainerBuilder();
cb.Populate(sc);
var ctnr = cb.Build();
var sp = new AutofacServiceProvider(ctnr);
var myObj = sp.GetRequiredService<IMyInterface>();
ctnr.Dispose(); // myObj is disposed - this is not correct!
}
The externally owned singleton is disposed on container disposal - this is not correct.
When using a lambda factory function, Autofac is working correctly:
private static void AutofacDI_2()
{
var sc = new ServiceCollection();
sc.AddSingleton<IMyInterface>(_ => new MyClass());
var cb = new ContainerBuilder();
cb.Populate(sc);
var ctnr = cb.Build();
var sp = new AutofacServiceProvider(ctnr);
var myObj = sp.GetRequiredService<IMyInterface>();
ctnr.Dispose(); // myObj is disposed
}
The singleton is disposed as expected.
Used versions:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="4.6.0" targetFramework="net46" />
<package id="Autofac.Extensions.DependencyInjection" version="4.1.0" targetFramework="net46" />
<package id="Microsoft.Extensions.DependencyInjection" version="1.1.1" targetFramework="net46" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="1.1.1" targetFramework="net46" />
</packages>
Issue Analytics
- State:
- Created 6 years ago
- Comments:7 (3 by maintainers)
Top Results From Across the Web
Dependency injection guidelines - .NET
Disposable transient services are captured by the container for disposal. This can turn into a memory leak if resolved from the top-level ...
Read more >Understanding Disposables In .NET Dependency Injection
If you have several disposable singleton instances created in this manner, they can all be disposed of within the one method.
Read more >design patterns - So Singletons are bad, then what?
The major concern with singleton is that it adds coupling and thus makes things like testing hard(er). You can reduce the coupling by...
Read more >Singleton pattern
In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to a singular instance.
Read more >Disposal — Autofac 7.0.0 documentation
If you have singleton components (registered as SingleInstance() ) they will live for the life of the container. Since container lifetimes are usually...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Disposable services are typically registered as scoped services, so the container is responsible for disposing them. Therefore I think an injected service should never be disposed by the instance that get the service injected. Only services that are created by an (injected) service factory should be disposed by the code that uses the factory.
From my perspective the issue comes from a gap in the overall design of dependency injection containers. That is not only an autofac issue. Often dependency containers work as a service registries and service factories at once. From the client side it is not possible to decide who is responsible for disposing an object. This detail is provided by the registration but not forwarded appropriately to the client. Or did I oversee something?