DEV Community

Cover image for Worker Services with Asp.Net Core
Fernando Sonego
Fernando Sonego

Posted on

Worker Services with Asp.Net Core

Since the release of Asp.Net Core 3 we have a new feature called Worker Services. These services are not really a novelty, the concept has been around for a long time and you have surely used it before when our web applications needed to run some task periodically in the background, such as a notification via email.

Before .Net Core 3.0 we could do them, but now from this version, we have templates available to create them. All these kinds of background tasks can also be implemented as a Windows Service or a Daemon in Linux.

In this post we will see the creation of a Worker Service that will periodically log something particular and how it will be consumed. For this we will use VS Code. We can use Visual Studio 2019. Let's get started.

Worker Services

To begin we create the template project from the console with the command:

dotnet new worker

Let's open the code with the VSCode we will see something like this:

Worker Service

We can see, after creating the project, we have 3 fundamentals files: appsettings.json, programs.cs, and worker.cs. Let's start by looking at the program.cs file. Inside we will find the configuration of how the service should be hosted and the configuration of its dependency injection.

public class Program
 {
     public static void Main(string[] args)
     {
         CreateHostBuilder(args).Build().Run();
     }

     public static IHostBuilder CreateHostBuilder(string[] args) =>
         Host.CreateDefaultBuilder(args)
             .ConfigureServices((hostContext, services) =>
             {
                 services.AddHostedService<Worker>();
             });
 }

If we need to add some extra service, for example, to send mail or some kind of notification, we will have to inject it from this section.

Let's now look at our worker.cs.

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: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }

We see that our class inherits from BackgroundService which is the one that implements IHostedService. You must implement ExecuteAsync which will be the method that runs in the background. We must bear in mind that we must always use the cancellation token to be able to quickly end and close the service correctly.

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: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }

We can run it to see how the default template works from the console with dotnet new. We will see how the service is called once per second.

Worker Service

We can implement service initialization and termination events to inform us of these events. For this, we will create the class WorkerExtend.cs

public class WorkerExtend : BackgroundService
    {
        private readonly ILogger<WorkerExtend> _logger;

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

        public override async Task StartAsync(CancellationToken cancellationToken)
        {           
            _logger.LogInformation("Worker Start: {time}", DateTimeOffset.Now);
            await base.StartAsync(cancellationToken);
        }

        public override async Task StopAsync(CancellationToken cancellationToken)
        {           
            _logger.LogInformation("Worker Stop: {time}", DateTimeOffset.Now);
            await base.StopAsync(cancellationToken);
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }

        public override void Dispose()
        {

        }
    }
}

We will execute it from the console and we will see the outputs of the corresponding events.

Worker Service

However, before commenting that we can implement it as a Windows Service. To do so, we just have to add the injection to do it in the program.cs file.

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseWindowsService()
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<WorkerExtend>();
                });

Then to implement it we can use the sc.exe tool in the following way.

sc.exe create DemoWS binpath= PathToThePublishFolder\YourWorkerClassName.exe
sc.exe start YourWorkerClassName

Conclusion

In previous versions of .Net Core we could work with Workers, let's keep that in mind. The advantage of this new template is that it allows us to have small separate services that are easy to maintain. Running these services in the background has a very low processing cost that makes them quite useful to use even if they are long running. Hope you enjoy this functionality. See you in the next post.

Top comments (0)