Skip to content

Commit 035750b

Browse files
authored
feat(db): implement options pattern for database connection strings
1 parent 83beb7c commit 035750b

10 files changed

Lines changed: 86 additions & 16 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace EvolutionaryArchitecture.Fitnet.Contracts.Data.Database;
2+
3+
using System.ComponentModel.DataAnnotations;
4+
5+
internal sealed class ContractsPersistenceOptions
6+
{
7+
public const string SectionName = "ConnectionStrings";
8+
9+
[Required] public string Contracts { get; init; } = string.Empty;
10+
}

Chapter-1-initial-architecture/Src/Fitnet/Contracts/Data/Database/DatabaseModule.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
namespace EvolutionaryArchitecture.Fitnet.Contracts.Data.Database;
22

33
using Microsoft.EntityFrameworkCore;
4+
using Microsoft.Extensions.Options;
45

56
internal static class DatabaseModule
67
{
7-
private const string ConnectionStringName = "Contracts";
8-
98
internal static IServiceCollection AddDatabase(this IServiceCollection services, IConfiguration configuration)
109
{
11-
var connectionString = configuration.GetConnectionString(ConnectionStringName);
12-
services.AddDbContext<ContractsPersistence>(options => options.UseNpgsql(connectionString));
10+
services.Configure<ContractsPersistenceOptions>(
11+
configuration.GetSection(ContractsPersistenceOptions.SectionName));
12+
services.AddOptionsWithValidateOnStart<ContractsPersistenceOptions>();
13+
services.AddDbContext<ContractsPersistence>((serviceProvider, options) =>
14+
{
15+
var persistenceOptions = serviceProvider.GetRequiredService<IOptions<ContractsPersistenceOptions>>();
16+
var connectionString = persistenceOptions.Value.Contracts;
17+
options.UseNpgsql(connectionString);
18+
});
1319

1420
return services;
1521
}
Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
namespace EvolutionaryArchitecture.Fitnet.Offers.Data.Database;
22

33
using Microsoft.EntityFrameworkCore;
4+
using Microsoft.Extensions.Options;
45

56
internal static class DatabaseModule
67
{
7-
private const string ConnectionStringName = "Offers";
8-
98
internal static IServiceCollection AddDatabase(this IServiceCollection services, IConfiguration configuration)
109
{
11-
var connectionString = configuration.GetConnectionString(ConnectionStringName);
12-
services.AddDbContext<OffersPersistence>(options => options.UseNpgsql(connectionString));
10+
services.Configure<OffersPersistenceOptions>(configuration.GetSection(OffersPersistenceOptions.SectionName));
11+
services.AddOptionsWithValidateOnStart<OffersPersistenceOptions>();
12+
services.AddDbContext<OffersPersistence>((serviceProvider, options) =>
13+
{
14+
var persistenceOptions = serviceProvider.GetRequiredService<IOptions<OffersPersistenceOptions>>();
15+
var connectionString = persistenceOptions.Value.Offers;
16+
options.UseNpgsql(connectionString);
17+
});
1318

1419
return services;
1520
}
@@ -20,4 +25,4 @@ internal static IApplicationBuilder UseDatabase(this IApplicationBuilder applica
2025

2126
return applicationBuilder;
2227
}
23-
}
28+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace EvolutionaryArchitecture.Fitnet.Offers.Data.Database;
2+
3+
using System.ComponentModel.DataAnnotations;
4+
5+
internal sealed class OffersPersistenceOptions
6+
{
7+
public const string SectionName = "ConnectionStrings";
8+
9+
[Required] public string Offers { get; init; } = string.Empty;
10+
}

Chapter-1-initial-architecture/Src/Fitnet/Passes/Data/Database/DatabaseModule.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
namespace EvolutionaryArchitecture.Fitnet.Passes.Data.Database;
22

33
using Microsoft.EntityFrameworkCore;
4+
using Microsoft.Extensions.Options;
45

56
internal static class DatabaseModule
67
{
7-
private const string ConnectionStringName = "Passes";
8-
98
internal static IServiceCollection AddDatabase(this IServiceCollection services, IConfiguration configuration)
109
{
11-
var connectionString = configuration.GetConnectionString(ConnectionStringName);
12-
services.AddDbContext<PassesPersistence>(options => options.UseNpgsql(connectionString));
10+
services.Configure<PassesPersistenceOptions>(configuration.GetSection(PassesPersistenceOptions.SectionName));
11+
services.AddOptionsWithValidateOnStart<PassesPersistenceOptions>();
12+
services.AddDbContext<PassesPersistence>((serviceProvider, options) =>
13+
{
14+
var persistenceOptions = serviceProvider.GetRequiredService<IOptions<PassesPersistenceOptions>>();
15+
var connectionString = persistenceOptions.Value.Passes;
16+
options.UseNpgsql(connectionString);
17+
});
1318

1419
return services;
1520
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace EvolutionaryArchitecture.Fitnet.Passes.Data.Database;
2+
3+
using System.ComponentModel.DataAnnotations;
4+
5+
internal sealed class PassesPersistenceOptions
6+
{
7+
public const string SectionName = "ConnectionStrings";
8+
9+
[Required] public string Passes { get; init; } = string.Empty;
10+
}

Chapter-1-initial-architecture/Src/Fitnet/Program.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
builder.Services.AddRequestsValidations();
1919
builder.Services.AddClock();
2020

21+
// Add modules - each module registers its own options with validation
2122
builder.Services.AddPasses(builder.Configuration);
2223
builder.Services.AddContracts(builder.Configuration);
2324
builder.Services.AddOffers(builder.Configuration);
24-
builder.Services.AddReports();
25+
builder.Services.AddReports(builder.Configuration);
2526

2627
await using var app = builder.Build();
2728

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace EvolutionaryArchitecture.Fitnet.Reports.DataAccess;
2+
3+
internal static class DataAccessModule
4+
{
5+
internal static IServiceCollection AddDataAccess(this IServiceCollection services, IConfiguration configuration)
6+
{
7+
services.Configure<ReportsPersistenceOptions>(configuration.GetSection(ReportsPersistenceOptions.SectionName));
8+
services.AddOptionsWithValidateOnStart<ReportsPersistenceOptions>();
9+
services.AddScoped<IDatabaseConnectionFactory, DatabaseConnectionFactory>();
10+
11+
return services;
12+
}
13+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace EvolutionaryArchitecture.Fitnet.Reports.DataAccess;
2+
3+
using System.ComponentModel.DataAnnotations;
4+
5+
internal sealed class ReportsPersistenceOptions
6+
{
7+
public const string SectionName = "ConnectionStrings";
8+
9+
[Required] public string Reports { get; init; } = string.Empty;
10+
}

Chapter-1-initial-architecture/Src/Fitnet/Reports/ReportsModule.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ namespace EvolutionaryArchitecture.Fitnet.Reports;
55

66
internal static class ReportsModule
77
{
8-
internal static IServiceCollection AddReports(this IServiceCollection services)
8+
internal static IServiceCollection AddReports(this IServiceCollection services, IConfiguration configuration)
99
{
10-
services.AddDataAccess();
10+
services.AddDataAccess(configuration);
1111
services.AddNewPassesRegistrationsPerMonthReport();
1212

1313
return services;

0 commit comments

Comments
 (0)