Passaggio all'ora solare 31 ottobre 2021 03:00 02:00 sposta indietro l'orologio di 1 ora (si dorme 1 ora in più)
In un servizio windows .NET 5 il file di log appsettings.json può essere letto come in una normale console application
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();
    ...
}   
ma con alcune attenzioni.

In particolare va prestata attenzione a come viene ricavato il percorso di esecuzione, usando il metodo
C#
System.IO.Directory.GetCurrentDirectory()
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
C#
// using Microsoft.Extensions.Hosting.WindowsServices;
WindowsServiceHelpers.IsWindowsService()
e condizionare il recupero del percorso
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 diventa
C#: 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;
}
Nel Worker posso leggere la configurazione tramite Dependency Injection (DI) IOptions<AppSettings>
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.Net5.WorkerService.
Potrebbe interessarti anche: