I'm playing with .net 5 dependency injection, which is very useful. From various sources, like the accepted answer of this question Dependency injection, inject with parameters , I learn that using a factory method in AddTransient/Scoped/Singleton, the container will not dispose the object created, even if the object implements IDisposable. But the following code tells me clearly that the objects are disposed automatically (which is great, but I'd like to understand the story behind).
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ConsoleDI.Example
{
public class TransientDisposable : IDisposable
{
public void Dispose() => Console.WriteLine($"{nameof(TransientDisposable)}.Dispose()");
}
public class ScopedDisposable : IDisposable
{
public void Dispose() => Console.WriteLine($"{nameof(ScopedDisposable)}.Dispose()");
}
public class SingletonDisposable : IDisposable
{
public void Dispose() => Console.WriteLine($"{nameof(SingletonDisposable)}.Dispose()");
}
class Program
{
static void Main(string[] args)
{
using IHost host = CreateHostBuilder(args).Build();
ExemplifyDisposableScoping(host.Services, "Scope 1");
Console.WriteLine();
ExemplifyDisposableScoping(host.Services, "Scope 2");
Console.WriteLine();
host.Run();
}
static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) =>
services.AddTransient<TransientDisposable>(_ => new TransientDisposable())
.AddScoped<ScopedDisposable>(_ => new ScopedDisposable())
.AddSingleton<SingletonDisposable>(_ => new SingletonDisposable())
);
static void ExemplifyDisposableScoping(IServiceProvider services, string scope)
{
Console.WriteLine($"{scope}...");
using IServiceScope serviceScope = services.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;
_ = provider.GetRequiredService<TransientDisposable>();
_ = provider.GetRequiredService<ScopedDisposable>();
_ = provider.GetRequiredService<SingletonDisposable>();
}
}
}
output:
Scope 1...
ScopedDisposable.Dispose()
TransientDisposable.Dispose()
Scope 2...
ScopedDisposable.Dispose()
TransientDisposable.Dispose()
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /home/zhjun/Work/tmp/ConsoleDI.Example
^Cinfo: Microsoft.Hosting.Lifetime[0]
Application is shutting down...
SingletonDisposable.Dispose()
So why is this? Is this a .net 5 DI container improvement? or is this always this way?