Since lesson 2.8.1, the Kladde can read tenders from TED, but only when someone calls the TedClient. Nobody in the office should have to press a button every morning. So the Kladde fetches the new notices itself, every night at three, and shows them in a list in the morning.

For that, the app needs work that runs without a request: a background service. In this lesson you learn how it starts, ticks, ends and crashes, how it gets to the database without remembering a connection forever, and how to test "every night at three" without waiting for a night.

Work without a request

An ASP.NET Core app is a host: at startup it starts a series of services and keeps them running until the app ends. One of them is the web server Kestrel. You write your own services of this kind with two building blocks from Microsoft.Extensions.Hosting:

  • IHostedService has two methods: StartAsync at startup and StopAsync at shutdown.
  • BackgroundService implements IHostedService for the most common case, a loop that runs until the app ends. You write only ExecuteAsync(CancellationToken stoppingToken). The token signals when the app shuts down.

You register such a service with builder.Services.AddHostedService<T>(). The container creates it once, so it's a singleton and lives as long as the app. Since .NET 10, ExecuteAsync runs on a thread of its own from its very first statement; a service that computes for a long time before it waits for the first time no longer holds up the start of the app.

A background service shares the process with the web app. That's convenient: it knows the same services, the same configuration and the same log. But it also means: if the process crashes or the app is deployed anew, the background is gone too, in the middle of its work. Lesson 2.8.4 is about that. In K3, the import then moves out of the process, through a queue.