Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement .net core IHostedService that runs one time only

Tags:

c#

.net-core

I know a IHostedService that runs only one time sounds like a console application, but the reason I want to use it instead of a plain console application is:

  • .net core introduces Generic Host for running non-http application
  • A plain console application does not have DI, Logger, Configurations ready to use

By using the following code, I'm able to somewhat achieve this one-time behaviour, however, I could not find a way to gracefully exit the app after it finishes.

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

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) => { services.AddHostedService<StartUp>(); });
    }

Where StartUp is a simple IHostedService

public class StartUp:IHostedService
    {
        private ILogger<StartUp> _logger;

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

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("start async");
            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("stop async");
            return Task.CompletedTask;
        }
    }

How can I stop the app gracefully? Or if this is completely wrong, how should I implement this one-time application?

like image 349
MiDaa Avatar asked Feb 27 '20 08:02

MiDaa


People also ask

What is IHostedService in .NET core?

Since . NET Core 2.0, the framework provides a new interface named IHostedService helping you to easily implement hosted services. The basic idea is that you can register multiple background tasks (hosted services) that run in the background while your web host or host is running, as shown in the image 6-26.

How do I stop BackgroundService in .NET core?

The BackgroundService token source is cancelled by the StopAsync method. So to cancel the CustomService async work you have to call the StopAsync method. This cancel token provided to the ExecuteAsync method as parameter.


2 Answers

Yes, you can achieve that by injecting IHostApplicationLifetime into your hosted service.

Example:

 public class StartUp:IHostedService
 {
    private readonly IHostApplicationLifetime _host;
    private ILogger<StartUp> _logger;

    public StartUp(IHostApplicationLifetime host, ILogger<StartUp> logger)
    {
        _host = host;
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("start async");
        _host.StopApplication();
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("stop async");
        return Task.CompletedTask;
    }
}

You can also set exit code by setting Environment.ExitCode.For example:

Environment.ExitCode = 0;
_host.StopApplication();
like image 53
OlegI Avatar answered Oct 10 '22 18:10

OlegI


If you are just using IHostedService as a workaround for the missing DI and ILogger you can also setup DI with the ILogger and IConfiguration directly without IHostedService

public class Program
{
    public static async Task Main(string[] args)
    {
        var configBuilder = new ConfigurationBuilder()
                                .AddJsonFile("appsettings.json", optional: true);
        var config = configBuilder.Build();

        var sp = new ServiceCollection()
            .AddLogging(b => b.AddConsole())
            .AddSingleton<IConfiguration>(config)
            .AddSingleton<IFooService, FooService>()
            .BuildServiceProvider();

        var logger = sp.GetService<ILoggerFactory>().CreateLogger<Program>();
        logger.LogDebug("Starting");

        var bar = sp.GetService<IFooService>();
        await bar.DoAsync();
    }
}

With this setup your code is just running once, can resolve every service you register in the ServiceCollection and no need of a Host to start

Example: .netfiddle

like image 11
Martin Avatar answered Oct 10 '22 17:10

Martin