Search code examples
c#.net-coredependency-injectionconsoleidisposable

.NET Core Dependency Injection container not calling Dispose()


I'm at a loss here with why Dispose() isn't being called when the program ends. I wrote a console application to facilitate testing an Azure Functions App. One of the classes implements IDisposable but Dispose() is never being called. I expanded my example to see if a dependent service that implements IDisposable has it's Dispose() method called and it does not. I just updated to 16.9.4 this morning and target framework is .NET 5. Here's the sample code:

public class CustomOptions
{
    public const string Section = "CustomSettings";
    public string Url { get; set; }
    public bool UseHttps { get; set; }
}
public interface IDisposableService
{
    void DoSomething();
}
public class DisposableService : IDisposableService, IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Disposing a disposable service");
        GC.SuppressFinalize(this);
    }

    public void DoSomething()
    {
        Console.WriteLine("I'm doing some work");
    }
}
public interface IConsoleService
{
    void Run(string[] args);
}
public class ConsoleService : IConsoleService, IDisposable
{
    private readonly ILogger<ConsoleService> _logger;
    private readonly CustomOptions _options;
    private readonly IDisposableService _service;


    public ConsoleService(IDisposableService service,
                          ILogger<ConsoleService> logger,
                          IOptions<CustomOptions> options)
    {
        _service = service;
        _logger = logger;
        _options = options.Value;
    }

    public void Dispose()
    {
        Console.WriteLine($"Disposing {nameof(ConsoleService)}");
        GC.SuppressFinalize(this);
    }

    public void Run(string[] args)
    {
        _logger.LogInformation("The application is now starting {Start}", DateTime.Now);
        Console.WriteLine($"The Url in the settings file is '{_options.Url}'");
        _service.DoSomething();
    }
}
class Program
{
    static void Main(string[] args)
    {
        IHost host = Host.CreateDefaultBuilder()
            .ConfigureServices((context, services) =>
            {
                services.Configure<CustomOptions>(
                    context.Configuration.GetSection(CustomOptions.Section));

                services.AddTransient<IDisposableService, DisposableService>();
                services.AddTransient<IConsoleService, ConsoleService>();

                services.AddLogging();
                services.AddOptions();
            })
            .Build();

        IConsoleService service =
            ActivatorUtilities.CreateInstance<ConsoleService>(host.Services);
        service.Run(args);
    }
}
appsettings.json
{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Warning"
        }
    },
    "CustomSettings": {
        "Url": "www.contoso.com",
        "UseHttps":  true
    }
}

The output from the program is:

info: ConsoleApp.ConsoleService[0]
The application is now starting 04/14/2021 10:30:54
The Url in the settings file is 'www.contoso.com'
I'm doing some work

Any help is greatly appreciated.


Changes to get it working are as follows:

  1. Add IDisposable to IConsoleService
public interface IConsoleService, IDisposable
{
    void Run(string[] args);
}
  1. and remove it from ConsoleService
public class ConsoleService : IConsoleService
{
  1. Wrap the IHost variable in a using statement or call host.Dispose()

  2. Use IServiceProvider.GetService<>() instead of ActivatorUtilities.CreateInstance<>()

    host.Services.GetService<IConsoleService>().Run(args);
    host.Dispose();

Solution

  • Here is an example just using the ServiceCollection

    IServiceCollection services = new ServiceCollection();
    services.AddSingleton<Application>(); // your application which implement RunAsync
    using var serviceProvider = services.BuildServiceProvider();
    await serviceProvider.GetService<Application>().RunAsync();