namespace EgwCoreLib.Lux.Data.Services.General { public class StatsCollectService : BackgroundService { #region Public Constructors public StatsCollectService( IConfiguration config, IServiceScopeFactory scopeFactory, ILogger logger) { _config = config; _scopeFactory = scopeFactory; _logger = logger; _clearProcessed = _config.GetValue("ServerConf:CleanupProcessed", false); _refreshPeriodMinutes = _config.GetValue("ServerConf:RefreshPeriodMinutes", 60); } #endregion Public Constructors #region Protected Methods protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("StatsCollectService started. RefreshPeriod={Minutes} clearProcessed={Clear}", _refreshPeriodMinutes, _clearProcessed); while (!stoppingToken.IsCancellationRequested) { try { // crea uno scope per risolvere servizi scoped (CalcRuidService incluso) await using var scope = _scopeFactory.CreateAsyncScope(); var calcRuid = scope.ServiceProvider.GetRequiredService(); // esegui l'operazione; passa lo stoppingToken se il metodo lo supporta await calcRuid.ExportStatsToDbAsync(_clearProcessed); } catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { // shutdown in corso: esci pulitamente _logger.LogInformation("StatsCollectService cancellation requested."); break; } catch (Exception ex) { // logga l'errore ma non lasciare che l'host si fermi _logger.LogError(ex, "Errore durante ExportStatsToDbAsync. Il servizio continuerà a riprovare."); } try { await Task.Delay(TimeSpan.FromMinutes(_refreshPeriodMinutes), stoppingToken); } catch (OperationCanceledException) { // shutdown: esci break; } } _logger.LogInformation("StatsCollectService stopping."); } #endregion Protected Methods #region Private Fields private readonly bool _clearProcessed; private readonly IConfiguration _config; private readonly ILogger _logger; private readonly int _refreshPeriodMinutes; private readonly IServiceScopeFactory _scopeFactory; #endregion Private Fields } }