< Summary

Information
Class: ProjectTemplate.Infrastructure.Data.Extensions.InfrastructureDataAccessServiceExtensions
Assembly: ProjectTemplate.Infrastructure
File(s): /home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Infrastructure/Data/Extensions/InfrastructureDataAccessServiceExtensions.cs
Line coverage
100%
Covered lines: 109
Uncovered lines: 0
Coverable lines: 109
Total lines: 220
Line coverage: 100%
Branch coverage
88%
Covered branches: 30
Total branches: 34
Branch coverage: 88.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
AddApplicationInfrastructureDataAccess(...)100%66100%
ResolveDataAccessRegistration(...)80%2020100%
ConfigureDataAccess(...)100%44100%
ConfigureProvider(...)100%44100%
get_Provider()100%11100%
get_ConnectionString()100%11100%
get_AuditStorageMode()100%11100%
get_EnableDetailedErrors()100%11100%
get_EnableEfCoreTraceBridge()100%11100%
get_IsDisabled()100%11100%
Enabled(...)100%11100%
Disabled(...)100%11100%

File(s)

/home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Infrastructure/Data/Extensions/InfrastructureDataAccessServiceExtensions.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using Microsoft.Extensions.Configuration;
 3using Microsoft.Extensions.DependencyInjection;
 4using Microsoft.Extensions.DependencyInjection.Extensions;
 5using Microsoft.Extensions.Logging;
 6using ProjectTemplate.Infrastructure.Data.Auditing;
 7using ProjectTemplate.Infrastructure.Data.ExternalLogins;
 8using ProjectTemplate.Infrastructure.Data.Options;
 9
 10namespace ProjectTemplate.Infrastructure.Data.Extensions;
 11
 12/// <summary>
 13/// Provides infrastructure-owned service registration methods for application data access.
 14/// </summary>
 15public static class InfrastructureDataAccessServiceExtensions
 16{
 17    /// <summary>
 18    /// Registers EF Core data access services for infrastructure and non-web consumers.
 19    /// </summary>
 20    public static IServiceCollection AddApplicationInfrastructureDataAccess(
 21        this IServiceCollection services,
 22        IConfiguration configuration)
 23    {
 21624        ArgumentNullException.ThrowIfNull(services);
 21625        ArgumentNullException.ThrowIfNull(configuration);
 26
 21627        services
 21628            .AddOptions<DataAccessOptions>()
 21629            .Bind(configuration.GetSection(DataAccessOptions.SectionName))
 29030            .Validate(options => !string.IsNullOrWhiteSpace(options.Provider),
 21631                "ProjectTemplate:DataAccess:Provider must not be empty.")
 29032            .Validate(options => DataAccessOptions.IsDisabledProvider(options.Provider)
 29033                || !string.IsNullOrWhiteSpace(options.ConnectionStringName),
 21634                "ProjectTemplate:DataAccess:ConnectionStringName must not be empty when data access is enabled.")
 29035            .Validate(options => options.Diagnostics is not null,
 21636                "ProjectTemplate:DataAccess:Diagnostics must be configured as an object when specified.")
 29037            .Validate(options => AuditStorageModes.IsSupported(options.Auditing.StorageMode),
 21638                "ProjectTemplate:DataAccess:Auditing:StorageMode must be Local, Outbox, or ExternalSink.")
 21639            .ValidateOnStart();
 40
 21641        DataAccessRegistration registration = ResolveDataAccessRegistration(configuration);
 20442        if (registration.IsDisabled)
 43        {
 1244            return services;
 45        }
 46
 19247        services.TryAddScoped<ICurrentActorAccessor, SystemCurrentActorAccessor>();
 19248        services.TryAddScoped<IApplicationAuditContextAccessor, SystemApplicationAuditContextAccessor>();
 19249        services.TryAddScoped<IApplicationAuditValuePolicy, DefaultApplicationAuditValuePolicy>();
 19250        services.TryAddSingleton<IApplicationMutationManifestBuilder, CanonicalApplicationMutationManifestBuilder>();
 19251        services.TryAddSingleton<IApplicationMutationManifestHasher, Sha256ApplicationMutationManifestHasher>();
 19252        services.TryAddSingleton<IApplicationMutationManifestVerifier, ApplicationMutationManifestVerifier>();
 53
 19254        if (AuditStorageModes.IsLocal(registration.AuditStorageMode))
 55        {
 19056            services.TryAddScoped<IApplicationAuditStore, LocalApplicationAuditStore>();
 57        }
 58
 19259        services.TryAddScoped<ContextIsolatedApplicationSaveChangesPipeline>();
 19260        services.TryAddScoped<IApplicationSaveChangesPipeline>(serviceProvider =>
 21261            serviceProvider.GetRequiredService<ContextIsolatedApplicationSaveChangesPipeline>());
 19262        services.TryAddScoped<IApplicationMutationAuditReceiptRegistry>(serviceProvider =>
 19663            serviceProvider.GetRequiredService<ContextIsolatedApplicationSaveChangesPipeline>());
 19264        services.TryAddScoped<IApplicationMutationAuditReceiptAccessor, ApplicationDbContextMutationAuditReceiptAccessor
 19265        services.TryAddScoped<ApplicationSaveChangesInterceptor>();
 66
 19267        services.AddDbContext<ApplicationDbContext>((serviceProvider, options) =>
 21468            ConfigureDataAccess(
 21469                serviceProvider,
 21470                options,
 21471                registration));
 72
 19273        services.AddDbContextFactory<ApplicationDbContext>(
 2074            (serviceProvider, options) => ConfigureDataAccess(
 2075                serviceProvider,
 2076                options,
 2077                registration),
 19278            ServiceLifetime.Scoped);
 79
 19280        services.TryAddScoped<IExternalLoginAccountResolver, EfCoreExternalLoginAccountResolver>();
 19281        return services;
 82    }
 83
 84    private static DataAccessRegistration ResolveDataAccessRegistration(IConfiguration configuration)
 85    {
 21686        DataAccessOptions dataAccessOptions = configuration
 21687            .GetSection(DataAccessOptions.SectionName)
 21688            .Get<DataAccessOptions>() ?? new DataAccessOptions();
 89
 21690        string provider = dataAccessOptions.Provider?.Trim() ?? string.Empty;
 21691        string connectionStringName = dataAccessOptions.ConnectionStringName?.Trim() ?? string.Empty;
 21692        string auditStorageMode = AuditStorageModes.Normalize(dataAccessOptions.Auditing.StorageMode);
 93
 21694        if (string.IsNullOrWhiteSpace(provider))
 95        {
 496            throw new InvalidOperationException("Application data access provider was not configured.");
 97        }
 98
 21299        if (!AuditStorageModes.IsSupported(auditStorageMode))
 100        {
 2101            throw new InvalidOperationException("Application audit storage mode was not configured with a supported valu
 102        }
 103
 210104        if (DataAccessOptions.IsDisabledProvider(provider))
 105        {
 12106            return DataAccessRegistration.Disabled(provider, auditStorageMode);
 107        }
 108
 198109        if (string.IsNullOrWhiteSpace(connectionStringName))
 110        {
 4111            throw new InvalidOperationException("Application data access connection string name was not configured.");
 112        }
 113
 194114        string connectionString = configuration.GetConnectionString(connectionStringName)
 194115            ?? throw new InvalidOperationException($"Connection string '{connectionStringName}' was not configured.");
 116
 192117        return DataAccessRegistration.Enabled(
 192118            provider,
 192119            connectionString,
 192120            auditStorageMode,
 192121            dataAccessOptions.Diagnostics.EnableDetailedErrors,
 192122            dataAccessOptions.Diagnostics.EnableEfCoreTraceBridge);
 123    }
 124
 125    private static void ConfigureDataAccess(
 126        IServiceProvider serviceProvider,
 127        DbContextOptionsBuilder options,
 128        DataAccessRegistration registration)
 129    {
 42130        ConfigureProvider(
 42131            options,
 42132            registration.Provider,
 42133            registration.ConnectionString);
 134
 40135        if (registration.EnableDetailedErrors)
 136        {
 4137            _ = options.EnableDetailedErrors();
 138        }
 139
 40140        if (registration.EnableEfCoreTraceBridge)
 141        {
 4142            ILogger<ApplicationDbContext> logger = serviceProvider
 4143                .GetRequiredService<ILogger<ApplicationDbContext>>();
 144
 4145            _ = options.LogTo(
 8146                message => EfCoreDiagnosticsLogging.LogEfCoreMessage(logger, message),
 4147                LogLevel.Trace);
 148        }
 40149    }
 150
 151    private static void ConfigureProvider(
 152        DbContextOptionsBuilder options,
 153        string provider,
 154        string connectionString)
 155    {
 42156        if (provider.Equals(DataAccessOptions.SqliteProvider, StringComparison.OrdinalIgnoreCase))
 157        {
 28158            options.UseSqlite(connectionString);
 28159            return;
 160        }
 161
 14162        if (provider.Equals(DataAccessOptions.SqlServerProvider, StringComparison.OrdinalIgnoreCase))
 163        {
 12164            options.UseSqlServer(connectionString);
 12165            return;
 166        }
 167
 2168        throw new InvalidOperationException(
 2169            $"Unsupported data access provider '{provider}'. Supported providers: {DataAccessOptions.SqliteProvider}, {D
 170    }
 171
 172    private readonly record struct DataAccessRegistration(
 42173        string Provider,
 42174        string ConnectionString,
 192175        string AuditStorageMode,
 40176        bool EnableDetailedErrors,
 40177        bool EnableEfCoreTraceBridge,
 204178        bool IsDisabled)
 179    {
 180        public static DataAccessRegistration Enabled(
 181            string provider,
 182            string connectionString,
 183            string auditStorageMode,
 184            bool enableDetailedErrors,
 185            bool enableEfCoreTraceBridge)
 186        {
 192187            return new(
 192188                provider,
 192189                connectionString,
 192190                auditStorageMode,
 192191                enableDetailedErrors,
 192192                enableEfCoreTraceBridge,
 192193                false);
 194        }
 195
 196        public static DataAccessRegistration Disabled(
 197            string provider,
 198            string auditStorageMode)
 199        {
 12200            return new(
 12201                provider,
 12202                string.Empty,
 12203                auditStorageMode,
 12204                false,
 12205                false,
 12206                true);
 207        }
 208    }
 209}
 210
 211internal static partial class EfCoreDiagnosticsLogging
 212{
 213    [LoggerMessage(
 214        EventId = 19000,
 215        Level = LogLevel.Trace,
 216        Message = "{EfCoreMessage}")]
 217    internal static partial void LogEfCoreMessage(
 218        ILogger logger,
 219        string efCoreMessage);
 220}