Learn how to create background services in .NET Core using the Generic Host. The Generic Host provides cross-cutting concerns that you're familiar with in ASP.NET Core, such as dependency injection, logging and configuration. This allows you to build long-running processes for non-HTTP scenarios.
.NET Core 2.1 introduced the Generic Host, which allows you to launch an app similar to that of ASP.NET Core but for processing non-HTTP requests.
Meaning, the generic host takes all the goodness that ASP.NET Core provides for cross-cutting concerns, such as its built-in dependency injection, logging and configuration, and allows you to build on top of that for non-HTTP scenarios.
The most typical scenario is for a worker service or any type of long-running process. This could be a service that does some work and then sleeps for a period of time before doing more work. An example of this would be a polling service to fetch data from an external web service. Another really common use case would be a service that pulls messages of a queue and processes them.
With .NET Core 3, there is a new template available that uses the Generic Host and gives you the basic scaffolding of creating a worker service.
If you're using the CLI, you can generate a new service worker easily:
The Generic Host library is the Microsoft.Extensions.Hosting NuGet package.
If you open up the .csproj
file generated by the template, you will see the package being referenced as well as the Microsoft.NET.Sdk.Worker
being referenced:
Program.cs
should look familiar if you've worked with ASP.NET Core:
You can configure various services just like you would in ASP.NET Core; Dependency Injection via AddSingleton()
or AddTransient()
; logging via AddLogging()
; or configuration via AddOptions()
.
The main difference is in ConfigureServices()
, where the new extension method, AddHostedService<T> where T : class, IHostedService
is called. This comes from the Microsoft.Extensions.Hosting.Abstractions package and is a transitive dependency from Microsoft.Extensions.Hosting
.
The new class that was created from the template is called Worker
and is used as the type parameter in AddHostedServices
.
The first thing to note is our class extends the abstract class, BackgroundService
. This is a new class provided in .NET Core 3. It implements IHostedService
, which is required by AddHostedService<T>
.
Before we jump into BackgroundService
, let's first take a look at IHostedService
and what has to get implemented, if you were creating your own implementation.
You simply need to implement the StartAsync()
and StopAsync()
methods using the CancellationToken
for graceful shutdown of your service.
You can probably already start to imagine how having an abstract class would be helpful, since the implementation would likely be similar for most scenarios where you want to create a long-running service.
BackgroundService
is new to .NET Core 3 and provides a simple abstract class for implementing a long-running service.
You simply need to implement Task ExecuteAsync(CancellationToken stoppingToken)
while handling the CancellationToken
that is used to determine when to stop your method.
You'll notice the constructor takes an ILogger<Worker>
as a dependency, which is resolved by the built-in dependency injection from the Generic Host. As mentioned, we can define other services to register with DI in the ConfigureServices()
method in Program.cs
.
For a simple example, I'm going to create a service that is going to make an HTTP call every hour to an exchange rate web service to get the latest USD to CAD exchange rate.
I'm adding the latest version of Microsoft.Extensions.Http
that provides us the ability to register the IHttpClientFactory
and register the Newtonsoft.Json
for deserializing the JSON response from the web service.
As mentioned, I'm calling AddHttpClient()
to register the IHttpClientFactory
which I can inject into my worker class:
The first thing is the IHttpClientFactory
so we can get a new instance of the HttpClient. If you're unfamiliar with the IHttpClientFactory
, check out the docs.
In the ExecuteAsync
, I'm going to make an HTTP request to the api.exchangeratesapi.io
service and get the latest exchange rate from USD to CAD. I'll handle relevant failures and use the logger to output. Likely here I'd be storing this data and persisting it somewhere for my application to use.
If you're running .NET Core in Windows, you can install this worker service as a Windows Service.
Add the Microsoft.Extensions.Hosting.WindowsServices
package to your .csproj
file as a PackageReference
This adds an extension method called UseWindowsService()
to the IHostBuilder
.
This allows you to run the application still as a console application or debug as you normally would through the CLI, Visual Studio, Visual Studio Code, Rider, etc. However, it also provides the ability to install (and then run it as a windows service).
The Generic Host and the new BackgroundService
in .NET Core 3 provides a convenient way to create long-running processes in .NET. You get all the wonderful features of dependency injection, logging, and configuration that you're used to in ASP.NET Core now for running long-running jobs or services.
Derek Comartin is software developer and Microsoft MVP with two decades of professional experience that span enterprise, professional services and product development. He’s written software for a variety of business domains, such as consumer goods, distribution, transportation, manufacturing, and accounting. He founded and leads the Windsor-Essex .NET Developers Group (@WENetDevelopers). Derek has a very active blog, codeopinion.com, that focuses on .NET, CQRS, Event Sourcing, HTTP APIs and Hypermedia.