< Summary

Information
Class: ProjectTemplate.Web.Services.ApplicationAuditCompletionOutboxHostedService
Assembly: ProjectTemplate.Web
File(s): /home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/Services/ApplicationAuditCompletionOutboxHostedService.cs
Line coverage
93%
Covered lines: 40
Uncovered lines: 3
Coverable lines: 43
Total lines: 84
Line coverage: 93%
Branch coverage
68%
Covered branches: 11
Total branches: 16
Branch coverage: 68.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)60%1010100%
ExecuteAsync()83.33%6690%

File(s)

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

#LineLine coverage
 1using Microsoft.Extensions.Options;
 2using ProjectTemplate.Infrastructure.Data.Auditing;
 3
 4namespace ProjectTemplate.Web.Services;
 5
 6/// <summary>
 7/// Dispatches durable audit-completion entries after their originating transaction commits.
 8/// </summary>
 39public sealed partial class ApplicationAuditCompletionOutboxHostedService(
 310    IServiceScopeFactory scopeFactory,
 311    IOptions<ApplicationAuditCompletionOutboxOptions> options,
 312    TimeProvider timeProvider,
 313    ILogger<ApplicationAuditCompletionOutboxHostedService> logger)
 14    : BackgroundService
 15{
 416    private readonly IServiceScopeFactory _scopeFactory =
 417        scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory));
 318    private readonly ApplicationAuditCompletionOutboxOptions _options =
 319        options?.Value ?? throw new ArgumentNullException(nameof(options));
 320    private readonly TimeProvider _timeProvider =
 321        timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
 322    private readonly ILogger<ApplicationAuditCompletionOutboxHostedService> _logger =
 323        logger ?? throw new ArgumentNullException(nameof(logger));
 24
 25    [LoggerMessage(
 26        EventId = 19100,
 27        Level = LogLevel.Error,
 28        Message = "The audit-completion outbox dispatch cycle failed and will be retried. ConsecutiveFailureCount: {Cons
 29    private static partial void LogDispatchCycleFailure(
 30        ILogger logger,
 31        Exception exception,
 32        int consecutiveFailureCount,
 33        double retryDelaySeconds);
 34
 35    /// <inheritdoc />
 36    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
 37    {
 238        if (!_options.Enabled)
 39        {
 040            return;
 41        }
 42
 243        int consecutiveFailureCount = 0;
 44
 345        while (!stoppingToken.IsCancellationRequested)
 46        {
 347            Exception? cycleFailure = null;
 48
 49            try
 50            {
 351                using IServiceScope scope = _scopeFactory.CreateScope();
 352                IApplicationAuditCompletionOutboxDispatcher dispatcher = scope.ServiceProvider
 353                    .GetRequiredService<IApplicationAuditCompletionOutboxDispatcher>();
 354                _ = await dispatcher.DispatchReadyAsync(stoppingToken).ConfigureAwait(false);
 055                consecutiveFailureCount = 0;
 056            }
 257            catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
 58            {
 259                break;
 60            }
 161            catch (Exception exception)
 62            {
 163                cycleFailure = exception;
 164                consecutiveFailureCount++;
 165            }
 66
 167            TimeSpan delay = BackgroundServiceRetryDelay.Calculate(
 168                _options.PollInterval,
 169                _options.MaximumCycleRetryDelay,
 170                consecutiveFailureCount);
 71
 172            if (cycleFailure is not null)
 73            {
 174                LogDispatchCycleFailure(
 175                    _logger,
 176                    cycleFailure,
 177                    consecutiveFailureCount,
 178                    delay.TotalSeconds);
 79            }
 80
 181            await Task.Delay(delay, _timeProvider, stoppingToken).ConfigureAwait(false);
 182        }
 283    }
 84}