.NET › Dependency Injection

How does the DI container's disposal behavior work for IDisposable and IAsyncDisposable services across different lifetimes?

Advanced

Answer

The DI container automatically tracks and disposes any IDisposable/IAsyncDisposable service it created, calling Dispose/DisposeAsync when the owning scope ends (for Scoped/Transient services resolved within a request scope) or when the application shuts down (for Singleton services) -- notably, Transient services are still tracked and disposed by their containing scope, not immediately after use, which can cause unexpected memory retention if many short-lived Transient disposables accumulate within one long-lived scope.
public class ReportExporter : IDisposable {
    public void Dispose() => Console.WriteLine("Disposed");
}

builder.Services.AddTransient<ReportExporter>();
// Even though Transient, if resolved many times within one request scope,
// ALL instances are held and disposed together only when that request scope ends
Real-world example A memory profiling session reveals unexpectedly high memory retention within long request scopes traced to hundreds of Transient IDisposable service instances all being held alive until the scope's end rather than being freed immediately after each use.

Common follow-ups: Why doesn't the container dispose Transient services immediately after use?;How would you work around this for a Transient disposable used many times in a loop?