12

Following this answer, I want to inject the IHostApplicationLifetime in my class to shutdown properly when the method StartAsync is over.

But I don't know how to get the applicationLifetime from the console and Inject it through de built-in dotnet core IoC container:

public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.SetBasePath(Directory.GetCurrentDirectory())
            .AddCommandLine(args)
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
        })
        .ConfigureServices((hostContext, services) =>
        {
            services.Configure<ConnectionStringConfiguration>(hostContext.Configuration.GetSection("ConnectionStrings"));
            services.AddTransient<ISmtpClient, MySmtpClient>();
            services.AddTransient<IEmailService, EmailService>();
            services.AddSingleton<IHostApplicationLifetime>(????); // What should I put here ????
            services.AddHostedService<EInvoiceSenderService>();
        })
        .UseSerilog();
}

Thank you !

Florian
  • 4,507
  • 10
  • 53
  • 73

2 Answers2

17

It is already added by default by the framework, so you should be able to access it from the hosted service as an injected dependency.

Simplified example

public class EInvoiceSenderService: IHostedService {
    private readonly ILogger logger;
    private readonly IHostApplicationLifetime appLifetime;

    public EInvoiceSenderService(
        ILogger<LifetimeEventsHostedService> logger, 
        IHostApplicationLifetime appLifetime) { //<--- INJECTED DEPENDENCY
        this.logger = logger;
        this.appLifetime = appLifetime;
    }

    public Task StartAsync(CancellationToken cancellationToken) {
        appLifetime.ApplicationStarted.Register(OnStarted);
        appLifetime.ApplicationStopping.Register(OnStopping);
        appLifetime.ApplicationStopped.Register(OnStopped);

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken) {
        return Task.CompletedTask;
    }


    private void OnStarted() {
        logger.LogInformation("OnStarted has been called.");

        // Perform post-startup activities here
    }

    private void OnStopping() {
        logger.LogInformation("OnStopping has been called.");

        // Perform on-stopping activities here
    }

    private void OnStopped() {
        logger.LogInformation("OnStopped has been called.");

        // Perform post-stopped activities here
    }    
}

Reference: .NET Generic Host: IHostApplicationLifetime

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Thank you !! Without your answer, I'll be searching again & again... – Florian Jan 08 '20 at 21:09
  • Even though the documentation says the same, it seems like you cannot register a method that returns "void". It has to return an Acion. Maybe something changed and the documentation wasn't updated? Idk. – user1339253 Apr 21 '21 at 16:46
  • 2
    I don't understand this answer, the code bears no resemblance to the code in the question. – Paul McCarthy Dec 01 '21 at 16:59
  • 3
    @PaulMcCarthy the code in the question is showing how they registered the `EInvoiceSenderService` service. The code in the answer shows an example of how the service is implemented. The OP was asking how to register `IHostApplicationLifetime` so that it can be injected into the service. The answer explains that `IHostApplicationLifetime` is automatically registered and that all they have to do is make sure it is in the constructor of the service. – Nkosi Dec 01 '21 at 17:11
  • @Nkosi This is all part of the .NET dependency injection system. What adds to my confusion, I'm trying to write a Windows service but most of the docs are around ASP.NET hosted services. OK, I'm now seeing they all share a lot of common stuff, but no one explicitly states this anywhere I can find. For this particular item, there's a list of stuff that can be "auto-injected" into your constructor as part of .NET Core: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-6.0#framework-provided-services – John Dyer Jun 24 '22 at 18:03
6

Previously (.net core 3.1), with use of startup class you can just throw additional parameters in the Configure method in a custom startup class.


...

WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
...

public class Startup
{
    public void Configure(
        IApplicationBuilder app,
        IWebHostEnvironment env,
        ILoggerFactory loggerFactory,
        IHostApplicationLifetime applicationLifetime,
        IHttpContextAccessor httpContextAccessor)
    {

        ...

        // Register method to be called when the application shuts down.
        applicationLifetime.ApplicationStopping.Register(OnShutdown);

With .net 6, the IHostApplicationLifetime is available from a property on the main app:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<IService, Service>();

var app = builder.Build();

IService service = app.Services.GetRequiredService<IService>();
ILogger logger = app.Logger;

// IHostApplicationLifetime is no longer obtained via dependency injection
IHostApplicationLifetime lifetime = app.Lifetime;  

IWebHostEnvironment env = app.Environment;

lifetime.ApplicationStarted.Register(() =>
    logger.LogInformation(
        $"The application {env.ApplicationName} started" +
        $" with injected {service}"));

See: https://learn.microsoft.com/en-us/aspnet/core/migration/50-to-60-samples?view=aspnetcore-7.0

BurnsBA
  • 4,347
  • 27
  • 39