< Summary

Information
Class: ProjectTemplate.Web.Services.ApplicationAuditReconciliationHostedService
Assembly: ProjectTemplate.Web
File(s): /home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/Services/ApplicationAuditReconciliationHostedService.cs
Line coverage
98%
Covered lines: 51
Uncovered lines: 1
Coverable lines: 52
Total lines: 93
Line coverage: 98%
Branch coverage
75%
Covered branches: 15
Total branches: 20
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)70%1010100%
ExecuteAsync()80%101097.43%

File(s)

/home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/Services/ApplicationAuditReconciliationHostedService.cs

#LineLine coverage
 1using Microsoft.Extensions.Options;
 2using ProjectTemplate.Infrastructure.Data.Auditing;
 3
 4namespace ProjectTemplate.Web.Services;
 5
 46public sealed partial class ApplicationAuditReconciliationHostedService(
 47    IServiceScopeFactory scopeFactory,
 48    IOptions<ApplicationAuditReconciliationOptions> options,
 49    TimeProvider timeProvider,
 410    ILogger<ApplicationAuditReconciliationHostedService> logger)
 11    : BackgroundService
 12{
 513    private readonly IServiceScopeFactory _scopeFactory =
 514        scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory));
 515    private readonly ApplicationAuditReconciliationOptions _options =
 516        options?.Value ?? throw new ArgumentNullException(nameof(options));
 417    private readonly TimeProvider _timeProvider =
 418        timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
 419    private readonly ILogger<ApplicationAuditReconciliationHostedService> _logger =
 420        logger ?? throw new ArgumentNullException(nameof(logger));
 21
 22    [LoggerMessage(
 23        EventId = 19110,
 24        Level = LogLevel.Error,
 25        Message = "The audit reconciliation cycle failed and will be retried. ConsecutiveFailureCount: {ConsecutiveFailu
 26    private static partial void LogReconciliationFailure(
 27        ILogger logger,
 28        Exception exception,
 29        int consecutiveFailureCount,
 30        double retryDelaySeconds);
 31
 32    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
 33    {
 234        if (!_options.Enabled || !_options.RunWorker)
 35        {
 036            return;
 37        }
 38
 239        int consecutiveFailureCount = 0;
 40
 441        while (!stoppingToken.IsCancellationRequested)
 42        {
 443            Exception? cycleFailure = null;
 44
 45            try
 46            {
 447                using IServiceScope scope = _scopeFactory.CreateScope();
 448                IApplicationAuditReconciler reconciler = scope.ServiceProvider
 449                    .GetRequiredService<IApplicationAuditReconciler>();
 450                _ = await reconciler.ReconcileAsync(stoppingToken).ConfigureAwait(false);
 51
 252                IApplicationAuditCompletionOutboxQuery? outboxQuery = scope.ServiceProvider
 253                    .GetService<IApplicationAuditCompletionOutboxQuery>();
 254                if (outboxQuery is not null)
 55                {
 256                    ApplicationAuditCompletionOutboxHealth deliveryHealth = await outboxQuery
 257                        .GetHealthAsync(stoppingToken)
 258                        .ConfigureAwait(false);
 159                    scope.ServiceProvider
 160                        .GetRequiredService<ApplicationAuditReconciliationMetrics>()
 161                        .UpdateDelivery(deliveryHealth);
 62                }
 63
 164                consecutiveFailureCount = 0;
 165            }
 266            catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
 67            {
 268                break;
 69            }
 170            catch (Exception exception)
 71            {
 172                cycleFailure = exception;
 173                consecutiveFailureCount++;
 174            }
 75
 276            TimeSpan delay = BackgroundServiceRetryDelay.Calculate(
 277                _options.Interval,
 278                _options.MaximumCycleRetryDelay,
 279                consecutiveFailureCount);
 80
 281            if (cycleFailure is not null)
 82            {
 183                LogReconciliationFailure(
 184                    _logger,
 185                    cycleFailure,
 186                    consecutiveFailureCount,
 187                    delay.TotalSeconds);
 88            }
 89
 290            await Task.Delay(delay, _timeProvider, stoppingToken).ConfigureAwait(false);
 291        }
 292    }
 93}