using Microsoft.EntityFrameworkCore; public sealed class WardGatewayMetricsCollector : BackgroundService { private readonly IServiceScopeFactory _scopes; private readonly ClinicalMetrics _metrics; private readonly ILogger _logger; public WardGatewayMetricsCollector( IServiceScopeFactory scopes, ClinicalMetrics metrics, ILogger logger) { _scopes = scopes; _metrics = metrics; _logger = logger; } protected override async Task ExecuteAsync(CancellationToken ct) { using var timer = new PeriodicTimer(TimeSpan.FromSeconds(60)); while (await timer.WaitForNextTickAsync(ct)) await CollectAsync(ct); } private async Task CollectAsync(CancellationToken ct) { try { await using var scope = _scopes.CreateAsyncScope(); var db = scope.ServiceProvider.GetRequiredService(); var offlineBySite = await db.WardGateways .Include(g => g.Site) .Where(g => g.Status != GatewayStatus.Online) .GroupBy(g => g.Site.SiteCode) .Select(g => new { SiteCode = g.Key, Count = g.Count() }) .ToListAsync(ct); foreach (var row in offlineBySite) _metrics.WardGatewaysOffline.WithLabels(row.SiteCode).Set(row.Count); var allGateways = await db.WardGateways .AsNoTracking() .ToListAsync(ct); foreach (var g in allGateways) _metrics.WardGatewayBufferDepth .WithLabels(g.GatewayCode, g.Department) .Set(g.ReportedBufferDepth); } catch (Exception ex) { _logger.LogError(ex, "WardGatewayMetricsCollector failed"); } } }