# Generic Host

Console apps or ASP.NET Core background tasks can use IHostedService to run. Such apps are also perfect to run in:

  • containers
  • system services (e.g. via systemd)

Kestrel

Kestrel itself uses IHostedService to run!

# IHostedService

# StartAsync

The StartAsync method of the IHostedService interface is async, but it's run inline during startup. If the hosted service is a long-running one, it should return a Task immediately and schedule the work on a separate thread. Using await is also not recommended.

# BackgroundService

The BackgroundService is a convenient base class. It's designed to be used with long-running tasks. It has just one method to override - ExecuteAsync(). We can freely use await and never-ending loops (although we should be checking CancellationToken).

# HttpClient and other non-singleton services

If our IHostedService implementation is long-lived we shouldn't inject HTTP services there (e.g. typed HttpClients). HttpClient should be short-lived. We could either use IServiceProvider to get new typed client in some interval (service locator - not ideal) or use IHttpClientFactory.

Singleton

In general, IHosterServices are singletons. If we need to use some service that is not a singleton from within, we shouldn't inject them directly. Instead, we need to use some factory or IServiceProvider (to create scope for example) to create the service repeatedly (usually the hosted service will run in some loop with a delay).

# Terminating the app

The hosted service can terminate the app when finished the execution. An instance of IHostApplicationLifetime needs to be injected and used:

_logger?.LogInformation("Terminating Application");
_applicationLifetime.StopApplication();

# Worker Service

Worker Service is a console app without the ASP.NET Core stuff.

We can easily create a Worker Service project with dotnet new worker. It creates a simple Worker class that inherits from BackgroundService and Program.cs that sets up IHost.

To get access to IHttpCLientFactory we need to add the Microsoft.Extensions.Http NuGet package.

# Systemd

To turn an app into a systemd service, we'd install the Microsoft.Extensions.Hosting.Systemd package. Then we'd call the UseSystemd() method on IHostBuilder.

TIP

Runing as a Windows service is similar, but different package/method combo is used.

ASP.NET Core

ASP.NET Core apps can be installed as services as well.

# Quartz.NET

Quartz.NET is a scheduler library that enable some more advanced features of background services, like:

  • CRON
  • running multiple instances of it, with options to control concurrency (similar to RedLock)

It acts as a

# Manual Creation

We can also create the boilerplate manually.

# Imports

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

# Creating an IHostBuilder

Simple:

private static IHostBuilder GetHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
        .ConfigureServices((hostContext, services) =>
        {
            services.AddSingleton(hostContext.Configuration.GetSection("AppConfiguration").Get<AppConfiguration>());
            services.AddHostedService<InfluxCompleteSetupService>();
        });
}

With configuration:

private static IHostBuilder GetHostBuilder()
{
    var builder = new HostBuilder()
        .ConfigureAppConfiguration((hostBuilderContext, configBuilder) =>
        {
            configBuilder.AddJsonFile("appsettings.json", optional: true);
            configBuilder.AddEnvironmentVariables();
        })
        .ConfigureServices((hostBuilderContext, services) =>
        {
            services.AddAllServices(hostBuilderContext.Configuration.GetSection("AppConfiguration").Get<AppConfiguration>());
        })
        .ConfigureLogging((hostBuilderContext, loggingBuilder) =>
        {
            loggingBuilder.AddConfiguration(hostBuilderContext.Configuration.GetSection("Logging"));
            loggingBuilder.AddConsole();
        });

    return builder;
}

# Running the application

static async Task Main(string[] args)
{
    try
    {
        var builder = GetHostBuilder(args);
        await builder.RunConsoleAsync(options => options.SuppressStatusMessages = true);
    }
    catch (Exception e)
    {
        Console.WriteLine("Program run into an exception");
        Console.WriteLine(e.Message);
        Console.WriteLine("Press any key to exit");
        Console.ReadKey();
    }
}

.NET 6:

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;

try {
    using var host = Host.CreateDefaultBuilder(args)
        .ConfigureServices((_, services) =>
            services
                .AddSingleton<InputReader>()
                .AddSingleton<BmiCalculator>()
                .AddHostedService<App>())
        .Build();
    
    await host.RunAsync();  
}
catch (Exception e)
{
    Console.WriteLine("Program run into an exception");
    Console.WriteLine(e.Message);
    Console.WriteLine("Press any key to exit");
    Console.ReadKey();
}
Last Updated: 10/5/2022, 6:26:29 PM