.Net Core and above (5,6,...) windows service / linux systemd

Search for a command to run...

No comments yet. Be the first to comment.
Just a very short blog post... for everyone who is not yet known to it. Since .Net Framework 4.6 for two immutable types we got new static fields in the framework: Array.Empty<T> string.Empty // instead of string foo = ""; byte[] bar = new byte[0...

Introduction In one of my older projects, I often had to synchronize and work with different parallel threads. Using async, parallel or 'real' threads does not make a real difference when it is coming to thread safety. As a very short short instructi...

In part III we take a closer look at where to upgrade our UserInterface from WindowsForms. As NyxSql was created as a learning project more than a decade ago, there is no MVVM or anything else implemented, which will make the upgrade more hard as the...

When using package manager with sources which require to authenticate yourself (e. g. internal sources or paid subscriptions), it may happen that you need to change your password or just entered the wrong credentials. Now, it is kinda like 'evil', be...

When working with multiple workstations (workstation / laptop) due to pandemic, we suddenly experienced the following error when opening solutions: Current solution contains incorrect configurations mappings. It may cause projects to not work correct...

While windows services had their own implementation classes in the .Net Framework, when migrating to .Net (Core) we noticed that we had to re-implement the whole service control.
Our requirements are, imho, basic requirements for a background windows service:
For integration we require the NuGet package Microsoft.Extensions.Hosting.WindowsServices. There is also one for the systemd part of Linux (Microsoft.Extensions.Hosting.Systemd), however I will cover that later.
We use the Program.cs for starting up the service. Normally, before I start the service itself, I verify the settings and so on. If we just return from the main method, the service will not start using the windows service controller and throw an error 'service could not be started'.
static async Task Main(string[] args)
{
// load settings / verify environment....
// start the service
using IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService(options =>
{
options.ServiceName = "MyServiceName";
})
.UseSystemd()
.ConfigureServices(services =>
{
services.AddHostedService<CustomService >();
})
.Build();
await host.RunAsync();
}
We start, by deriving our CustomService.cs from BackgroundService where we must override the ExecuteAsync method:
public class CustomService : BackgroundService
{
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
throw new NotImplementedException();
}
}
The stoppingToken is cancelled when the service is stopped by the service controller. It is brought to use by the dependency injection, what we can also use to get a reference to the IHostApplicationLifetime from the constructor, which we save us for later in the class itself AND directly register to a delegate which is getting executed when the application is stopping:
private readonly IHostApplicationLifetime _appLifeTime;
/// <summary> constructor </summary>
public CustomService(IHostApplicationLifetime appLifeTime)
{
_appLifeTime = appLifeTime;
_appLifeTime.ApplicationStopping.Register(AppStopping);
}
private void AppStopping()
{
}
By the delegate registration we do not require to loop, checking the stoppingToken, for the current state.
By using the IHostApplicationLifetime we can stop ourselves pretty simple:
_appLifeTime.StopApplication();
Be aware, that the delegate ApplicationStopping is also executed!
The previous windows service installation was based on a 'service installer' which was executed after starting the 'installutil.exe' from the .net framework folder
This is not anymore valid, so we use the New-Service cmdlet, which also has got much more options than the previous method using the 'service installer'.
To use the cmdlet, take a look here. I even prefer this method about invoke the windows apis directly.
On Linux we require a systemd services file. Here we will create a system service called 'sample-dotnet':
vi /etc/systemd/system/sample-dotnet.service
Content:
# reload: systemctl deamon-reload
# enable: systemctl enable itxprdsrv.service
[Unit]
Description=sample dotnet service
[Service]
User=opc
Type=simple
ExecStart=/opt/sample-dotnet
TimeoutStopSec=2
[Install]
WantedBy=multi-user.target
Afterwards, reload and enable systemd:
systemctl deamon-reload
systemctl enable sample-dotnet.service
Naturally you have to set the executable the execute permission:
chmod +x /opt/sample-dotnet
If you have SELinux enabled, this will block it. In development environments we can work around by:
semanage fcontext -a -t bin_t 'sample-dotnet'