Search code examples
.net-corebackground-serviceihostedservice

BackgroundService QueueHostedService


I am attempting to use the BackgroundService and IBackgroundTaskQueue to have a service monitoring a queue to do work. I've started with the sample in the MS docs here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-5.0&tabs=visual-studio#queued-background-tasks

My problem is it doesn't appear like my QueueHostedService is ever started. None of my breakpoints get hit for it. For now I was using the same "MonitorLoop" concept to queue work, that is starting and working fine to queue records, but they never get Dequeued and nothing in the QueueHostedService gets hit (Constructor, ExecuteAsync, BackgroundProcessing).

My sample code is the exact same as the documentation as far as I can tell with the following modifications: The monitor loop is async, but it is starting up fine so I don't think that is the problem. QueueHostedService and BackgroundTaskQueue are exactly the same as their samples. The adding of the services looks pretty much the same to me:

 /// <summary>
 /// Create host and services
 /// </summary>
 /// <param name="args"></param>
 /// <returns></returns>
 public static IHostBuilder CreateHostBuilder(string[] args) =>
     Host.CreateDefaultBuilder(args)
         .ConfigureServices((hostContext, services) =>
         {
             services.Configure<SQLSettingsModel>(Configuration.GetSection(key: SQLSettingsModel.SQLSettingsKey));                                        
             services.AddSingleton<MonitorLoop>();
             services.AddHostedService<QueuedHostedService>();
             services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
             services.AddSingleton<ISQLDataAccessService, SQLDataAccessService>();
             })
            .UseSerilog();
    }

This is what my Main method in Program.cs looks like:

/// <summary>
/// Start run loop
/// </summary>
/// <param name="args"></param>
public static async Task<int> Main(string[] args)
{
    Log.Logger = new LoggerConfiguration()               
       .Enrich.FromLogContext()               
       .WriteTo.Async(a => a.Console(
           outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}"))
       .WriteTo.Async(a => 
           a.File("WorkerService.log", 
               rollingInterval: RollingInterval.Day, 
               outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}"))
       .CreateLogger();
    try
    {
        Log.Information("Starting Worker");
        var host = CreateHostBuilder(args).Build();                
        var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
        await monitorLoop.StartMonitorLoopAsync();
    }
    catch (Exception err)
    {
        Log.Fatal(err, "Host terminated unexpectedly");
    }
    finally
    {
        Log.CloseAndFlush();
    }
    return 1;
}

Solution

  • The code creates a host but never runs it. It only retrieves the MonitorLoop service and runs a method on it.

    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    

    The documentation example runs the host by calling Run. The call to monitorLoop.StartMonitorLoop is not awaited, otherwise it would be impossible to call Run

    public static void Main(string[] args)
    {
        var host =CreateHostBuilder(args).Build();
        
        var monitorLoop = host.Services.GetRequiredService<MonitorLoop>();
        monitorLoop.StartMonitorLoop();
    
        host.Run();
    }