.netvisual-studiodocker-desktop

Cannot hit breakpoints in graceful shutdown stop events in .NET


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.

Program.cs

builder.Services.AddHostedService<MyBackgroundSvc>();

MyBackgroundSvc.cs

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.");
        }
    }
}

Solution

  • 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:

    Dockerfile (added withprocps stage)

    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...
    

    .csproj

    <PropertyGroup>
        <DockerfileFastModeStage>withprocps</DockerfileFastModeStage>
    </PropertyGroup>
    

    MyBackgroundSvc.cs

    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;
    }