I'm unable to hit stop events, such as StoppingAsync
and IHostApplicationLifetime.ApplicationStopping
, from BackgroundService
while debugging in Visual Studio 2022 (17.12.3) and docker desktop (4.36.0).
I've tested this with the default razor pages template (with docker support) plus the code below and my expectation is that I would be able to hit breakpoints in the stopping / stopped methods, but that doesn't happen.
builder.Services.AddHostedService<MyBackgroundSvc>();
public class MyBackgroundSvc(ILogger<MyBackgroundSvc> logger,
IHostApplicationLifetime appLifetime) : BackgroundService, IHostedLifecycleService
{
// Start... events are executed, but Stop... ones are not.
public Task StartingAsync(CancellationToken cancellationToken)
{
logger.LogInformation("STARTING_ASYNC was called.");
return Task.CompletedTask;
}
public Task StartedAsync(CancellationToken cancellationToken)
{
logger.LogInformation("STARTED_ASYNC was called.");
return Task.CompletedTask;
}
public Task StoppingAsync(CancellationToken cancellationToken)
{
logger.LogInformation("STOPPING_ASYNC was called.");
return Task.CompletedTask;
}
public Task StoppedAsync(CancellationToken cancellationToken)
{
logger.LogInformation("STOPPED_ASYNC was called.");
return Task.CompletedTask;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
appLifetime.ApplicationStopping.Register(() =>
logger.LogInformation("APPLICATION_STOPPING was called."));
await Task.Delay(2000, stoppingToken);
}
catch (Exception ex)
{
logger.LogError(ex, "Error during ExecuteAsync work.");
}
}
}
This is specific to the interaction between Visual Studio and Docker Desktop.
Based on the feedback from the here: https://github.com/microsoft/DockerTools/issues/451
The answer, at the time of writing, is that "stop debugging will just terminate the process, and no additional code will get to run. Closing the browser will be equivalent to stop debugging."
Thanks to the help of Gregg Miskelly and Nathan Carlson, a workaround is to execute kill with the target (application) process id. The following adds procps
package in an additional docker stage (where it's not already available) so that it can be called from the command line:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER $APP_UID
WORKDIR /app
EXPOSE 8080
EXPOSE 8081
FROM base AS withprocps
USER root
RUN apt-get update && apt-get install -y procps
USER $APP_UID
# remaining stages...
<PropertyGroup>
<DockerfileFastModeStage>withprocps</DockerfileFastModeStage>
</PropertyGroup>
public Task StartingAsync(CancellationToken cancellationToken)
{
logger.LogInformation("STARTING_ASYNC was called.");
// Log cmd for easy copy/paste
var processId = Process.GetCurrentProcess().Id;
logger.LogInformation($"GRACEFUL SHUTDOWN COMMAND:\ndocker exec RazorPages kill -2 {processId}");
return Task.CompletedTask;
}