How to Build Windows Service with Worker Service and ASP.NET Core

Introduction

What is a Worker Service? It’s an ASP.NET Core template that allows you to create hosted long-running background services. These background services implement the IHostedService interface, so they are called “hosted services”. What is interesting is that dependency injection is available natively, which was not the case with a Windows Service created with TopShelf. They can be deployed as a Windows service but also Linux daemons. In this article we will see how to create and deploy a Worker Service as a Windows Service.

Create a Worker Service

To create a Worker Service, select in Visual Studio 2019 Worker Service then the desired version of ASP.NET Core (you can use the lastest ASP.NET Core 6).

The default template looks like this:

Program.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace WorkerService1
{
    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>();
                });
    }
}

Worker.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace WorkerService
{
    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);
            }
        }
    }
}

appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

As you can see, this is very basic. This template doen’t implement all BackgroundService methods, StartAsyncStopAsync and Dispose are missing. Your new Worker Service should look like this:

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace WorkerService
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;

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

        public override async Task StartAsync(CancellationToken cancellationToken)
        {
            // DO YOUR STUFF HERE
            await base.StartAsync(cancellationToken);
        }

        public override async Task StopAsync(CancellationToken cancellationToken)
        {
            // DO YOUR STUFF HERE
            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()
        {
            // DO YOUR STUFF HERE
        }
    }
}

Deploy a Worker Service as a Windows service

First thing to do is to install this package, because it’s not installed by default in the ASP.NET Core 3 Worker Service template:

Once done add UseWindowsService extension method on IHostBuilder like this:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

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

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

Your project is now ready to deploy.

Let’s see now how we can deploy you Windows Service.

In this series of example the Windows service will take the name of the Worker Service class

There are two ways:

  • Using sc.exe Tool
  • Publishing an exe file

Step 1: Publish the Worker service

cd "Path to the Worker service project"
dotnet restore
dotnet publish -o PathToThePublishFolder

Step 2: Deploy and start with sc utility

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

Step 3: Stop and delete with sc utility

sc.exe stop YourWorkerClassName 
sc.exe delete YourWorkerClassName

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *