C# Hosted Service

A simple hosted service running in a console application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Program
{
static void Main(string[] args)
{
var task = new HostBuilder()
.ConfigureLogging((context, builder) =>
{
builder.AddConsole();
})
.ConfigureAppConfiguration((context, builder) =>
{
builder
.AddEnvironmentVariables()
.AddCommandLine(args);
})
.ConfigureServices((hostBuilderContext, services) =>
{
services.AddHostedService<MyBackgroundService>();
services.AddHostedService<MyHostedService>();
})
.RunConsoleAsync();

task.Wait();
}
}

MyBackgroundService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

namespace Worker.Console
{
public class MyBackgroundService : BackgroundService
{
private readonly ILogger<MyBackgroundService> _logger;

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

protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("BBBBBBBBBBBBBBBBBBBBBBB MyBackgroundService ExecuteAsync");
return Task.CompletedTask;
}
}
}

MyHostedService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

namespace Worker.Console
{
public class MyHostedService : IHostedService
{
private readonly ILogger<MyHostedService> _logger;

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

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

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

FooService

This will create a worker that does some background work. The complete code for these snippets is at https://github.com/carlpaton/ThreadingDemo/tree/main/src/HostedService.Ca

  1. Create the empty console application
  2. Add Microsoft.Extensions.Hosting
  3. Create FooService which inherits and implements BackgroundService
1
2
3
4
5
6
7
public class FooService : BackgroundService
{
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
throw new System.NotImplementedException();
}
}
  1. Update Program.cs to add your worker/service to as a hosted service with .AddHostedService. My service below is FooService
1
2
3
4
5
6
7
8
9
10
11
static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<FooService>();
});
  1. Update FooService to do something, here we just write to the console every 2 seconds. Microsoft suggest the following things you could use this for
  • A background task polling a database looking for changes.
  • A scheduled task updating some cache periodically.
  • An implementation of QueueBackgroundWorkItem that allows a task to be executed on a background thread.
  • Processing messages from a message queue in the background of a web app while sharing common services such as ILogger.
  • A background task started with Task.Run().
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class FooService : BackgroundService
{
private readonly int _millisecondsTimeout;

public FooService()
{
_millisecondsTimeout = 2000; // the value would have been injected with IOptions/Settings
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//_logger.LogDebug($"FooService is starting.");

while (!stoppingToken.IsCancellationRequested)
{
//_logger.LogDebug($"FooService task doing background work.");

SimulateSomeWorker();

//await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
await Task.Delay(_millisecondsTimeout);
}

//_logger.LogDebug($"GracePeriod background task is stopping.");
}

private void SimulateSomeWorker()
{
Console.WriteLine("Worker was run {0}", DateTime.Now);
}

“By default, the cancellation token is set with a 5 seconds timeout, although you can change that value when building your WebHost using the UseShutdownTimeout extension of the IWebHostBuilder. This means that our service is expected to cancel within 5 seconds otherwise it will be more abruptly killed.”

References