Creare un servizio windows in .NET 5 con appsettings.json
In un servizio windows .NET 5 il file di log appsettings.json può essere letto come in una normale console application
ma con alcune attenzioni.
In particolare va prestata attenzione a come viene ricavato il percorso di esecuzione, usando il metodo
Tutto funziona finché siamo in debug o eseguiamo l'exe direttamente, ma le cose cambiano quando è in esecuzione come servizio windows.
In questo caso il metodo ritorna sempre C:\Windows\system32 indipendentemente da dove effettivamente è posizionato l'exe.
Quindi dobbiamo rivedere come viene recuperato il percorso basandoci sulla modalità di esecuzione, altrimenti sarà impossibile accedere al file appsettings.json.
Possiamo capire se l'applicativo viene eseguito come servizio windows tramite il metodo
e condizionare il recupero del percorso
Nel Worker posso leggere la configurazione tramite Dependency Injection (DI) IOptions<AppSettings>
Vedi l'esempio completo su GitHub - Sgart.Net.WorkerService.
C#: Program.cs
public static async Task<int> Main(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(System.IO.Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).Build();
...
}
In particolare va prestata attenzione a come viene ricavato il percorso di esecuzione, usando il metodo
C#
System.IO.Directory.GetCurrentDirectory()
In questo caso il metodo ritorna sempre C:\Windows\system32 indipendentemente da dove effettivamente è posizionato l'exe.
Quindi dobbiamo rivedere come viene recuperato il percorso basandoci sulla modalità di esecuzione, altrimenti sarà impossibile accedere al file appsettings.json.
Possiamo capire se l'applicativo viene eseguito come servizio windows tramite il metodo
C#
// using Microsoft.Extensions.Hosting.WindowsServices;
WindowsServiceHelpers.IsWindowsService()
C#
static bool _isService = WindowsServiceHelpers.IsWindowsService();
// ATTENZIONE nel caso di servizio windows, System.IO.Directory.GetCurrentDirectory() ritorna sempre C:\Windows\system32
static string _currentDirectory = _isService
? AppDomain.CurrentDomain.BaseDirectory
: System.IO.Directory.GetCurrentDirectory();
Esempio
A questo punto il codice per l'accesso alla configurazione diventaC#: Program.cs
public class Program
{
static bool _isService = WindowsServiceHelpers.IsWindowsService();
// ATTENZIONE nel caso di servizio windows, System.IO.Directory.GetCurrentDirectory() ritorna sempre C:\Windows\system32
static string _currentDirectory = _isService
? AppDomain.CurrentDomain.BaseDirectory
: System.IO.Directory.GetCurrentDirectory();
public static async Task<int> Main(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(_currentDirectory)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
try
{
await CreateHostBuilder(args).Build().RunAsync();
return 0;
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
var configuration = hostContext.Configuration;
// mappo la sezione "Settings" da usare con DI IOptions<AppSettings>
services.Configure<Models.AppSettings>(configuration.GetSection("Settings"));
// registro il Worker
services.AddHostedService<Worker>();
})
.UseWindowsService(); // imposto come Windows Service;
}
C#: Worker.cs
// using Microsoft.Extensions.Options;
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly AppSettings _settings;
public Worker(ILogger<Worker> logger, IOptions<AppSettings> settings)
{
_logger = logger;
// leggo il valore dalla configurazione
_settings = settings.Value;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
_logger.LogInformation($"WorkerPauseSeconds: {_settings.WorkerPauseSeconds}");
await Task.Delay(_settings.WorkerPauseSeconds * 1000, stoppingToken);
}
}
}
Vedi l'esempio completo su GitHub - Sgart.Net.WorkerService.