Skip to main content

How to create Windows service run periodically with .Net Core BackgroundService



 

In this post:

  • Create a windows service project with appsetting.json, connect database, and dependency injection.
  • Create and install windows service.
Create a windows service project with appsetting.json, connect database, and dependency injection.
  1. Create a new project.
  2. Select Worker Service. Select Next.
  3. Provide a project name in the Project name field or accept the default project name. Select Create.
  4. In the Create a new Worker service dialog, select Create.
You the project is created you will see a file named Worker.cs.
DoWork method is where you going to implement your service logic.
private async Task DoWork(CancellationToken stoppingToken)

But because Worker implemented BackgroundService so you can not inject singletons into the worker directly. So the workaround way is to implement DoWork method in a different service and use IServiceProvider to create a scope to put it back in Worker.cs. It's a little hard to understand right now but you will understand when you see the code.

Create an interface IWorkerScope implemented DoWork method.
Create a class named WorkerScope implemented IWorkerScope. 

WokerScope is now where you going to implement your service logic and you can freely use dependency injection here.

At this point, you should have these 3 files: Woker.cs, IWorkerScope.cs and WorkerScope.cs.


This is my worker class, you can see I use IServiceProvider to create a scope inside the DoWork method so now the DoWork is actually defined in WorkerScope.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace myService
{
    public class WorkerBackgroundService
    {
        private readonly IServiceProvider _services;


        public Worker(IServiceProvider services)
        {
            _services = services;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            await DoWork(stoppingToken);
        }

        private async Task DoWork(CancellationToken stoppingToken)
        {
            using var scope = _services.CreateScope();
            var scopedProcessingService =
                scope.ServiceProvider
                    .GetRequiredService<IWorkerScope>();

            await scopedProcessingService.DoWork(stoppingToken);
        }

        public override async Task StopAsync(CancellationToken stoppingToken)
        {
            await base.StopAsync(stoppingToken);
        }
    }
}


This is the IWorkerScope interface:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace myService
{
    internal interface IWorkerScope
    {
        Task DoWork(CancellationToken stoppingToken);
    }
}


This is the WorkerScope class:
As you can see I injected ApiService, in your case, it might be services to get data from a database or some services.
Here I just write current datetime to a file named resutl.txt. You can do whatever you want here.
The delay time decides how frequently you want your service to run.
Here my service runs every 1 second.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace myService
{
    internal class WorkerScopeIWorkerScope
    {
        private ApiService _apiService;
        public WorkerScope(ApiService apiService) {
            _apiService = apiService;
        }

        public async Task DoWork(CancellationToken stoppingToken) {
            while (!stoppingToken.IsCancellationRequested)
            {
                // write current datetime to result.txt every 1 second.
                using (StreamWriter writer = File.CreateText("result.txt"))
                {
                    writer.WriteLine(_apiService.GetData().ToString());
                }
                await Task.Delay(1000stoppingToken);
            }
        }
    }
}

Another important part is your Program.cs. This is where you add your database connections, services,.. just like the startup.cs in a normal web project.

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using myService.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

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

        public static IHostBuilder CreateHostBuilder(string[] args=>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((contextbuilder=>
                {
                    builder
                        .AddJsonFile("appsettings.json")
                        .AddEnvironmentVariables()
                        .Build();
                })
                .ConfigureServices((hostContextservices=>
                {
                    // add your connnections, I use Postgresql you can use other database like SQL server, it should work the same.
                    // if you using Postgresql as well, you need to install Npgsql.EntityFrameworkCore.PostgreSQL in nuget.
                    services.AddDbContext<ApplicationDbContext>((IServiceProvider serviceProviderDbContextOptionsBuilder options=>
                        options.UseNpgsql(hostContext.Configuration.GetSection("AppConfig:ConnectionString").Value)
                    );

                    //add your services like repositories, APIs, services.
                    services.AddScoped<ApiService>();

                    //add the worker.
                    services.AddScoped<IWorkerScopeWorkerScope>();
                    services.AddHostedService<Worker>();
                }).UseWindowsService();
    }
}


Don't forget to UseWindowsService(); // (install Microsoft.Extensions.Hosting.WindowsServices in nuget);
Now run your service you should see the datetime written in result.txt changing every 1 second.
So that's your service. Next, I will show you how to install your service as a windows service

Create and install windows service.

First, you need to build your project.
Right-click on your project, select Publish, select Folder, select Next, choose your folder location and click Finish. Click Publish.

To create a windows service:

Run Command prompt as administrator.

Run this command:

sc create YourServiceName BinPath= "_path_to_your_service_folder\myService.exe"

Now right-click on the taskbar, select Task Manager, select tab Services and verify if your service is created.

If you want to delete the service use
sc delete YourServiceName 

It depends on your Current root path, result.txt might be created inside your service folder or  C:\Windows\System32.

In some cases when you use Environment variables or do anything related to user permission, you might want to config permission by right-clicking on the service, select Open services, find your service, right-click, select Properties, in tab Log on, select Choose this account and browse the account you want to use.

Comments

Popular posts from this blog

How to use Angular RouteReuseStrategy to create snapshots so you can back to previous pages without reloading data

Github of this  exmaple.  There are a lot of practical scenarios when you load a list of items then go to the detail of an item, when you hit the back button you don't want to reload the item list again. Angular provides you a method to do exactly that. With RouteReuseStrategy when you hit the back button it takes you right back to where you were before without reloading anything. Of course, you can choose to do this or not for any page and it super easy to set up. To do this you need to create a class implement RouteReuseStrategy from @angular/router. There are 5 methods you need to implement: shouldDetach, store, shouldAttach, retrieve, shouldReuseRoute. Let's take a closer look at these methods. shouldDetach shouldDetach ( route :  ActivatedRouteSnapshot ):  boolean When you go to the next page, this method will be trigged, it decides whether the current route (a page) should be detached. In other words, Angular takes a snapshot of it and saves it in memory ...

SEO with Angular, add title, meta keywords, a must-do thing for your website

  For those who don't know what SEO is, SEO stands for Search Engine Optimization, it helps people discover your site through search engines like Google, Bing, ... Take a look at the below picture: To let search engines discover your site, you must let search engines know your site, for google you can use Google Search Console  but that's another topic, I assume search engines already know your site. The 3 most important factors are URL, title tag, and meta tag (description, keyword,...). Search engines will look for those 3 things (and many other factors) to compare with the search query and decide whether your site is relevant or not. Url Try to use meaningful URLs. For example, instead of using a URL like https://yoursite.com/products/1 use https://yoursite.com/products/iphone or even better https://yoursite.com/iphone . You can achieve this by configuring your rooting modules. To make slug URLs you can strip accents your product names and join them with a hyphen (-). For e...

Create and deploy multi language (i18n) website with Angular 8+

This guide should work with Angular 8+.  In  this post: 1. Create an angular web project. 2. Set up i18n and translate. 3. Deploy the website on Windows using IIS. In this guide, I am using 2 languages, the setup for more than 2 languages should be similar. You can find the code of this guide in my github . 1. Create an angular web project. To create an angular app, in command  prompt type: ng new multiLanguageApp Open "app.component.html" delete the default code and paste this code : < div >    < a   href = "/" > de </ a > &nbsp;    < a   href = "/en" > en </ a > </ div > < div > Danke! </ div > Run "ng serve" to serve your app. Go to localhost:4200 you should see: I am creating an app with German as the default language and English as the second language. "Danke" is a word in German means "Thank you". When we click "de" or "en" in the UI to switch langu...