0

I have a .NET 6 console app.

Program.cs code:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        var builder = Host.CreateApplicationBuilder(args);
        builder.Services.AddHostedService<FooService>();
        builder.Build().Run();
    }
}

public class FooService : BackgroundService
{
    readonly ILogger<FooService> _logger;

    public FooService(ILogger<FooService> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("FooService Executed.");
    }
}

Screen capture:

enter image description here

I want FooService to start at last when everything is already done, please help.

0

3 Answers 3

3

I'll bring your attention to this comment on Microsoft.Extensions.Hosting.Internal.Hosting.StartAsync(), which is the internal call that happens during your builder.Build().Run().

/// <summary>
/// Order:
///  IHostLifetime.WaitForStartAsync
///  Services.GetService{IStartupValidator}().Validate()
///  IHostedLifecycleService.StartingAsync
///  IHostedService.Start
///  IHostedLifecycleService.StartedAsync
///  IHostApplicationLifetime.ApplicationStarted
/// </summary>

Your ExecuteAsync() is there in the middle during IHostedService.Start. The "Application started. Press Ctrl+C.. etc" happen last, during IHostApplicationLifetime.ApplicationStarted. More specifically, from ConsoleLifetime.OnApplicationStarted.

private void OnApplicationStarted()
{
    Logger.LogInformation("Application started. Press Ctrl+C to shut down.");
    Logger.LogInformation("Hosting environment: {EnvName}", Environment.EnvironmentName);
    Logger.LogInformation("Content root path: {ContentRoot}", Environment.ContentRootPath);
}

Since that happens at the bottom, there's really nothing in the Host that comes after. Unless you start things outside of the Host.


If you want to have something to happen after all other IHostedServices have started, the best you can do is the 2nd last item on that list: IHostedLifecycleService.StartedAsync.

Which means instead of using BackgroundService, you directly implement IHostedLifecycleService

var builder = Host.CreateApplicationBuilder(args);
builder.Services
  .AddHostedService<AfterService>()
  .AddHostedService<Svc1>()
  .AddHostedService<Svc2>();
builder.Build().Run();

public class AfterService : IHostedLifecycleService
{
    private readonly ILogger<AfterService> _logger;
    public AfterService(ILogger<AfterService> logger) => _logger = logger;

    public Task StartingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
    public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;

    public async Task StartedAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("This will run after ALL other IHostedServices.Start have been called");
        while (!cancellationToken.IsCancellationRequested)
        {
            await Task.Delay(1000); // do stuff
        }
    }

    public Task StoppingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
    public Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
Sign up to request clarification or add additional context in comments.

Comments

1

Another way is to pass the IHostApplicationLifetime interface as your dependency and register an event after the application starts. You can still see the log message before anything else, but the execution of the logger is after the application starts.

public class FooService : BackgroundService
{
    private readonly ILogger<FooService> _logger;
    private readonly IHostApplicationLifetime _hostApplicationLifetime;

    public FooService(
        ILogger<FooService> logger,
        IHostApplicationLifetime hostApplicationLifetime)
    {
        _logger = logger;
        _hostApplicationLifetime = hostApplicationLifetime;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var appStarted = new TaskCompletionSource();
        _hostApplicationLifetime.ApplicationStarted.Register(() =>
        {
            appStarted.SetResult();
        });

        await appStarted.Task;
        if (appStarted.Task.IsCompletedSuccessfully)
        {
            _logger.LogInformation("FooService Executed.");
        }
    }
}

It's just a simple example but you can imagine now the possibilities.

Comments

1

Use IHostedService over Background service. Background services works first. There is an example for your case:

(Start task working after service initialize)

Program.cs

static CancellationTokenSource _tokenSource { get; set; }

static async Task Main(string[] args)
{
    _tokenSource = new();


    await Microsoft.Extensions.Hosting.Host
        .CreateDefaultBuilder(args)
        .UseWindowsService(options =>
            options.ServiceName = "Test")
        .ConfigureServices((hostContext, services) =>
    {
        services.AddSingleton<ISchedulerService, QuartzScheduler>();
        services.AddHostedService<QuartzWorker>();
    }).Build().RunAsync(_tokenSource.Token);
}

QuartzWorker.cs

public class QuartzWorker(ISchedulerService schedulerService) : IHostedService, IDisposable
{
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        await schedulerService.Init(cancellationToken);
        await schedulerService.Start(cancellationToken);
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        await schedulerService.Stop(cancellationToken);
    }

    void IDisposable.Dispose()
    {
        GC.SuppressFinalize(this);
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.