< Summary

Information
Class: ProjectTemplate.Web.HealthChecks.ApplicationDatabaseHealthCheck
Assembly: ProjectTemplate.Web
File(s): /home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/HealthChecks/ApplicationDatabaseHealthCheck.cs
Line coverage
96%
Covered lines: 29
Uncovered lines: 1
Coverable lines: 30
Total lines: 85
Line coverage: 96.6%
Branch coverage
75%
Covered branches: 12
Total branches: 16
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(...)100%44100%
CheckHealthAsync()83.33%66100%
IsMissingSqliteDatabase(...)50%6687.5%

File(s)

/home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/HealthChecks/ApplicationDatabaseHealthCheck.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using Microsoft.Extensions.Diagnostics.HealthChecks;
 3using Microsoft.Extensions.Options;
 4using ProjectTemplate.Infrastructure.Data;
 5using ProjectTemplate.Web.Options;
 6
 7namespace ProjectTemplate.Web.HealthChecks;
 8
 9/// <summary>
 10/// Reports whether the application database accepts connections.
 11/// </summary>
 12/// <remarks>
 13/// Registered with the <see cref="ApplicationHealthCheckTags.Ready"/> tag so an instance that cannot reach its database
 14/// is removed from load balancing. The result description never includes connection strings or provider exception
 15/// text. For SQLite, a database file that has not been created yet is reported as unavailable. When
 16/// <see cref="ApplicationHealthCheckOptions.DatabaseReadinessCheckEnabled"/> is <see langword="false"/>, the check
 17/// reports healthy without contacting the database.
 18/// </remarks>
 819public sealed class ApplicationDatabaseHealthCheck(
 820    IServiceScopeFactory scopeFactory,
 821    IOptionsMonitor<ApplicationHealthCheckOptions> options)
 22    : IHealthCheck
 23{
 24    internal const string AvailableDescription = "The application database is reachable.";
 25
 26    internal const string UnavailableDescription = "The application database is not reachable.";
 27
 28    internal const string DisabledDescription = "The application database readiness check is disabled.";
 29
 1030    private readonly IServiceScopeFactory _scopeFactory =
 1031        scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory));
 932    private readonly IOptionsMonitor<ApplicationHealthCheckOptions> _options =
 933        options ?? throw new ArgumentNullException(nameof(options));
 34
 35    /// <summary>
 36    /// Checks whether the application database accepts connections.
 37    /// </summary>
 38    /// <param name="context">The health check context.</param>
 39    /// <param name="cancellationToken">A token that cancels the check.</param>
 40    /// <returns>The database health result.</returns>
 41    public async Task<HealthCheckResult> CheckHealthAsync(
 42        HealthCheckContext context,
 43        CancellationToken cancellationToken = default)
 44    {
 845        ArgumentNullException.ThrowIfNull(context);
 846        cancellationToken.ThrowIfCancellationRequested();
 47
 848        if (!_options.CurrentValue.DatabaseReadinessCheckEnabled)
 49        {
 650            return HealthCheckResult.Healthy(DisabledDescription);
 51        }
 52
 253        using IServiceScope scope = _scopeFactory.CreateScope();
 254        ApplicationDbContext dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
 55
 256        if (IsMissingSqliteDatabase(dbContext))
 57        {
 158            return new HealthCheckResult(context.Registration.FailureStatus, UnavailableDescription);
 59        }
 60
 161        bool canConnect = await dbContext.Database
 162            .CanConnectAsync(cancellationToken)
 163            .ConfigureAwait(false);
 64
 165        return canConnect
 166            ? HealthCheckResult.Healthy(AvailableDescription)
 167            : new HealthCheckResult(context.Registration.FailureStatus, UnavailableDescription);
 868    }
 69
 70    private static bool IsMissingSqliteDatabase(ApplicationDbContext dbContext)
 71    {
 272        if (!string.Equals(
 273                dbContext.Database.ProviderName,
 274                "Microsoft.EntityFrameworkCore.Sqlite",
 275                StringComparison.Ordinal))
 76        {
 077            return false;
 78        }
 79
 280        string dataSource = dbContext.Database.GetDbConnection().DataSource;
 81
 282        return !string.IsNullOrWhiteSpace(dataSource) &&
 283            !string.Equals(dataSource, ":memory:", StringComparison.OrdinalIgnoreCase) && !File.Exists(dataSource);
 84    }
 85}