Worker Service Template in .NET Core 3.0
We take a look at a new addition to the .NET Core framework, the worker service template, and just how it works in our C# code.
Join the DZone community and get the full member experience.
Join For FreeWith ASP.NET Core 3.0 Preview 3, we get a template for background processes like Windows services and Linux daemons. The new project template is called Worker Service and this post shows how it works and how to use it.
Creating a Worker Service Application
After downloading .NET Core 3.0 Preview 3, open Visual Studio 2019, create new ASP.NET Core web application, and select Worker service as the project type.
NB! Currently, Worker Service is located under ASP.NET Core web applications and the template uses the web SDK. This will change in the future as the intent of the development team is not to have any dependencies to other SDKs by default.
Default .NET Core Worker Service
With default implementation of the worker service, we get two classes: Program and Worker. The Program class is very straightforward and a little bit similar to what we have in web applications. The difference is that instead of a startup class we have a worker class and it is registered as a hosted service.
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
});
}
To make things more interesting, compare the code here with the code shown in my post Running an ASP.NET Core Application as a Windows Service.
The Worker class is also very laconic. An interesting thing to notice is that it offers support for .NET Core dependency injection. Notice how the logger is given as a constructor argument of the Worker class. The worker class we get when creating new worker service project writes out current time after every second.
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation($"Worker running at: {DateTime.Now}");
await Task.Delay(1000, stoppingToken);
}
}
}
Without any additional configuration changes, we can run the application to see it work.
Background Service Class
The Worker service class inherits from BackgroundService defined in Microsoft.Extensions.Hosting.Abstractions package. We can see that there are additional methods we can override: StartAsync()
, StopAsync()
, and Dispose()
. StartAsync()
and StopAsync()
are inherited from the IHostedService interface.
public abstract class BackgroundService : IHostedService, IDisposable
{
public virtual void Dispose();
public virtual Task StartAsync(CancellationToken cancellationToken);
public virtual Task StopAsync(CancellationToken cancellationToken);
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
}
We can use the StartAsync()
method to initialize our worker service and StopAsync()
to clean up when the worker is closing. If the worker made use of any disposable resources, then these resources must be disposed in the Dispose()
method that is the latest point for this.
Extending the Worker Service
As I had sample code already open in Visual Studio, I thought I'd see what happens when I override other methods of BackgroundService and write time to the log in the same way it was done with the ExecuteAsync()
method.
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
public override Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation($"Worker started at: {DateTime.Now}");
return base.StartAsync(cancellationToken);
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation($"Worker running at: {DateTime.Now}");
return Task.CompletedTask;
}
public override Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation($"Worker stopped at: {DateTime.Now}");
return base.StopAsync(cancellationToken);
}
public override void Dispose()
{
_logger.LogInformation($"Worker disposed at: {DateTime.Now}");
base.Dispose();
}
}
And here is the result of running our extended worker service. Take a close look at the output.
We can see here all the worker service methods that were called. An interesting thing to notice is that the worker service started before the application starts and it stopped after the application stopped. This is explained in the official document, Background tasks with hosted services in ASP.NET Core.
Project File
Just for interest, I also checked out what's inside the project file. Well, nothing special, I must say.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0-preview3.19153.1" />
</ItemGroup>
</Project>
There's a reference to netcoreapp3.0 and the Microsoft.Extensions.Hosting NuGet package. So, no mysteries or surprises — everything is neat and clean.
Wrapping Up
The Worker service template is a good addition to .NET Core and it's good to see that the process hosting gets more and more generalized in .NET Core. At the same time, we still have some great features, like dependency injection, available. Perhaps the most interesting part is to see how the worker service will be used as a Windows service and Linux daemon and how much work it is to use.
Published at DZone with permission of Gunnar Peipman, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments