X

Worker Service template in .NET Core 3.0

With ASP.NET Core 3.0 Preview 3 we have template for background processes like Windows services and Linux daemons. New project template is called Worker Service and this writing shows how it works and how to use it.

Creating 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 project type.

NB! Currently Worker Service is located under ASP.NET Core web applications and the template uses web SDK. This will change in the future as the intent by development team is not to have any dependencies to other SDK-s by default.

Default .NET Core worker service

With default implementation of worker service we get two classes: Program and Worker. Program class is very straightforward and a little bit similar to what we have in web applications. The difference is that instead of startup class we have 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 one shown in my post Running ASP.NET Core application as Windows service.

Worker class is also very laconic. Interesting thing to notice is support for .NET Core dependency injection. Notice how logger is given as constructor argument of 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

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 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 StartAsync() method to initialize our worker service and StopAsync() to clean up when worker is closing. If worker made use of any disposable resources then these resources must be disposed in Dispose() method that is latest point for this.

Extending worker service

As I had sample code already open in Visual Studio then I thought to try out what happens when I override other methods of BackgroundService too and write time to log same way as it is done in 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 to output.

We can see here all worker service methods called. Interesting thing to notice is that worker service is started before application starts and it’s stopped after application stops. It’s explained at official document Background tasks with hosted services in ASP.NET Core.

Project file

Just for interest I also checked out what’s inside project file. Well, nothing special, I want to 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 reference to netcoreapp3.0 and Microsoft.Extensions.Hosting NuGet package. So, no mysteries and surprises – everything is neat and clean.

Wrapping up

Worker service template is good addition to .NET Core and it’s good to see that process hosting gets more and more generalized in .NET Core. Same time we have still all powerful features like dependency injection available. Perhaps most interesting is to see how worker service will be used as Windows service and Linux daemon and how much work it is to use same codebase on both platforms. From ASP.NET Core 3.0 Preview 3 announcement we can read that samples by Microsoft are coming soon.

Liked this post? Empower your friends by sharing it!
Categories: .NET

View Comments (21)

  • In your StopAsync method, you're calling StartAsync... this will probably have unintended consequences ;)

    I didn't know about the BackgroundService class, I was implementing the start/stop logic myself... Thanks for the tip!

  • I was wondering how to implement a daemon in linux considering all the interruptions commands and stuff... If all of that is transparent or do we need to code something special for linux daemons...

  • I'm not yet sure how it will be with stable version of .NET Core 3.0. To make something work as system background service we need some integration points with operating system modules that run background processes. It means dependencies to native libraries. I hope .NET Core 3.0 comes out with generalizations where there is common way how background services are controlled and how we can communicate with external processes that can send commands to services. Let's see what next previews put on the table.

  • I think every service on Azure is okay where you can run console application. Over coming weeks Microsoft should publish some articles that provide technical details about how to run worker services as Windows service and Linux daemon. I think we should wait until this information is published.

  • You have Greats and useful posts, please implement something like a rating or "I like it" to leave positive feedback, i want to show you that i liked a specific post but i don't want to write a comment

  • Thank you Gunnar, great article. I've noticed that if an exception happens, for example, the background service dies but the host process keeps running. Any recommendations on how to recover in such cases?

  • Great Article!!! Thank you.
    I have a quick question. Can worker service be called from angular website while hosted as a windows service?

  • If your worker process has HTTP server implemented then it is possible. Theoretically - I have not tried it yet - you should be able to host also ASP.NET Core web application as Windows service.

  • This should be easy to run as a daemon on Linux with systemd, but what about running it as a Windows service? Can we still use installutil to set it up as a service on Windows or there needs to be a workaround?

  • the article is really useful for all beginners as well as the professionals .
    my ask is is there any possible way we can invoke the worker process on the click events .
    for example adding any value from the application and on success invoke the worker service executeasync task .
    open for the suggestion for the better approach .

  • It's possible to write unit tests for BackgroundService but the question is if you really need it. Background services are usually using some service classes that carry the functionalities that services runs on schedule. Those service classes are the ones you need to cover with unit tests.

Related Post