Skip to content

Commit db632c3

Browse files
committed
Refactor SmtpService to use new dbcontextfactory (#190)
1 parent 1c53add commit db632c3

22 files changed

+87
-36
lines changed

Diff for: docs/misc/dev/upgrade-ef-server-model.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
layout: default
3+
title: Upgrade the AliasClientDb EF model
4+
parent: Development
5+
grand_parent: Miscellaneous
6+
nav_order: 3
7+
---
8+
9+
# Upgrade the AliasServerDb EF model
10+
11+
The AliasServerDb EF model has migrations for both the SQLite and PostgreSQL databases. This means
12+
that when you make changes to the EF model, you need to create migrations for both databases.
13+
14+
1. Make migration for PostgreSQL database:
15+
```bash
16+
dotnet ef migrations add InitialMigration --context AliasServerDbContextPostgresql --output-dir Migrations/PostgresqlMigrations
17+
```
18+
19+
2. Make migration for SQLite database:
20+
```bash
21+
dotnet ef migrations add InitialMigration --context AliasServerDbContextSqlite --output-dir Migrations/SqliteMigrations
22+
```

Diff for: src/AliasVault.Admin/Main/Components/WorkerStatus/ServiceControl.razor

+3-3
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
/// </summary>
103103
private static bool IsHeartbeatValid(DateTime lastHeartbeat)
104104
{
105-
return DateTime.Now <= lastHeartbeat.AddMinutes(5);
105+
return DateTime.UtcNow <= lastHeartbeat.AddMinutes(5);
106106
}
107107

108108
/// <summary>
@@ -205,10 +205,10 @@
205205
entry.DesiredStatus = newDesiredStatus;
206206
await dbContext.SaveChangesAsync();
207207

208-
var timeout = DateTime.Now.AddSeconds(30);
208+
var timeout = DateTime.UtcNow.AddSeconds(30);
209209
while (true)
210210
{
211-
if (DateTime.Now > timeout)
211+
if (DateTime.UtcNow > timeout)
212212
{
213213
return false;
214214
}

Diff for: src/Databases/AliasServerDb/Configuration/DatabaseConfiguration.cs

+11-2
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,26 @@ public static IServiceCollection AddAliasVaultDatabaseConfiguration(this IServic
2525
{
2626
var dbProvider = configuration.GetValue<string>("DatabaseProvider")?.ToLower() ?? "sqlite";
2727

28+
// Add custom DbContextFactory registration which supports multiple database providers.
2829
switch (dbProvider)
2930
{
3031
case "postgresql":
31-
services.AddScoped<IAliasServerDbContextFactory, PostgresqlDbContextFactory>();
32+
services.AddSingleton<IAliasServerDbContextFactory, PostgresqlDbContextFactory>();
3233
break;
3334
case "sqlite":
3435
default:
35-
services.AddScoped<IAliasServerDbContextFactory, SqliteDbContextFactory>();
36+
services.AddSingleton<IAliasServerDbContextFactory, SqliteDbContextFactory>();
3637
break;
3738
}
3839

40+
// Updated DbContextFactory registration
41+
services.AddDbContextFactory<AliasServerDbContext>((sp, options) =>
42+
{
43+
var factory = sp.GetRequiredService<IAliasServerDbContextFactory>();
44+
factory.ConfigureDbContextOptions(options); // Let the factory configure the options directly
45+
});
46+
47+
// Add scoped DbContext registration based on the factory
3948
services.AddScoped<AliasServerDbContext>(sp =>
4049
{
4150
var factory = sp.GetRequiredService<IAliasServerDbContextFactory>();

Diff for: src/Databases/AliasServerDb/IAliasServerDbContextFactory.cs

+8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
namespace AliasServerDb;
99

10+
using Microsoft.EntityFrameworkCore;
11+
1012
/// <summary>
1113
/// The AliasServerDbContextFactory interface.
1214
/// </summary>
@@ -18,6 +20,12 @@ public interface IAliasServerDbContextFactory
1820
/// <returns>The AliasServerDbContext.</returns>
1921
AliasServerDbContext CreateDbContext();
2022

23+
/// <summary>
24+
/// Configures the DbContext options.
25+
/// </summary>
26+
/// <param name="optionsBuilder">The DbContextOptionsBuilder.</param>
27+
void ConfigureDbContextOptions(DbContextOptionsBuilder optionsBuilder);
28+
2129
/// <summary>
2230
/// Creates a new AliasServerDbContext asynchronously.
2331
/// </summary>

Diff for: src/Databases/AliasServerDb/Migrations/PostgresqlMigrations/20241222101415_InitialMigration.Designer.cs renamed to src/Databases/AliasServerDb/Migrations/PostgresqlMigrations/20241222185633_InitialMigration.Designer.cs

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: src/Databases/AliasServerDb/Migrations/PostgresqlMigrations/20241222101415_InitialMigration.cs renamed to src/Databases/AliasServerDb/Migrations/PostgresqlMigrations/20241222185633_InitialMigration.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
265265
{
266266
Id = table.Column<int>(type: "integer", nullable: false)
267267
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
268-
ServiceName = table.Column<string>(type: "varchar", maxLength: 255, nullable: false),
268+
ServiceName = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
269269
CurrentStatus = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
270270
DesiredStatus = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
271271
Heartbeat = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)

Diff for: src/Databases/AliasServerDb/Migrations/PostgresqlMigrations/AliasServerDbContextPostgresqlModelSnapshot.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
696696
b.Property<string>("ServiceName")
697697
.IsRequired()
698698
.HasMaxLength(255)
699-
.HasColumnType("varchar");
699+
.HasColumnType("character varying(255)");
700700

701701
b.HasKey("Id");
702702

Diff for: src/Databases/AliasServerDb/PostgresqlDbContextFactory.cs

+11-5
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,7 @@ public PostgresqlDbContextFactory(IConfiguration configuration)
3030
public AliasServerDbContext CreateDbContext()
3131
{
3232
var optionsBuilder = new DbContextOptionsBuilder<AliasServerDbContext>();
33-
var connectionString = _configuration.GetConnectionString("AliasServerDbContext");
34-
35-
optionsBuilder
36-
.UseNpgsql(connectionString, options => options.CommandTimeout(60))
37-
.UseLazyLoadingProxies();
33+
ConfigureDbContextOptions(optionsBuilder);
3834

3935
return new AliasServerDbContextPostgresql(optionsBuilder.Options);
4036
}
@@ -44,4 +40,14 @@ public Task<AliasServerDbContext> CreateDbContextAsync(CancellationToken cancell
4440
{
4541
return Task.FromResult(CreateDbContext());
4642
}
43+
44+
/// <inheritdoc/>
45+
public void ConfigureDbContextOptions(DbContextOptionsBuilder optionsBuilder)
46+
{
47+
var connectionString = _configuration.GetConnectionString("AliasServerDbContext");
48+
49+
optionsBuilder
50+
.UseNpgsql(connectionString, options => options.CommandTimeout(60))
51+
.UseLazyLoadingProxies();
52+
}
4753
}

Diff for: src/Databases/AliasServerDb/SqliteDbContextFactory.cs

+7-2
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,21 @@ public SqliteDbContextFactory(IConfiguration configuration)
2727
}
2828

2929
/// <inheritdoc/>
30-
public AliasServerDbContext CreateDbContext()
30+
public void ConfigureDbContextOptions(DbContextOptionsBuilder optionsBuilder)
3131
{
32-
var optionsBuilder = new DbContextOptionsBuilder<AliasServerDbContext>();
3332
var connectionString = _configuration.GetConnectionString("AliasServerDbContext") +
3433
";Mode=ReadWriteCreate;Cache=Shared";
3534

3635
optionsBuilder
3736
.UseSqlite(connectionString, options => options.CommandTimeout(60))
3837
.UseLazyLoadingProxies();
38+
}
3939

40+
/// <inheritdoc/>
41+
public AliasServerDbContext CreateDbContext()
42+
{
43+
var optionsBuilder = new DbContextOptionsBuilder<AliasServerDbContext>();
44+
ConfigureDbContextOptions(optionsBuilder);
4045
return new AliasServerDbContextSqlite(optionsBuilder.Options);
4146
}
4247

Diff for: src/Services/AliasVault.SmtpService/Handlers/DatabaseMessageStore.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace AliasVault.SmtpService.Handlers;
2626
/// <param name="logger">ILogger instance.</param>
2727
/// <param name="config">Config instance.</param>
2828
/// <param name="dbContextFactory">IDbContextFactory instance.</param>
29-
public class DatabaseMessageStore(ILogger<DatabaseMessageStore> logger, Config config, IDbContextFactory<AliasServerDbContext> dbContextFactory) : MessageStore
29+
public class DatabaseMessageStore(ILogger<DatabaseMessageStore> logger, Config config, IAliasServerDbContextFactory dbContextFactory) : MessageStore
3030
{
3131
/// <summary>
3232
/// Override the SaveAsync method to save the email into the database.

Diff for: src/Services/AliasVault.SmtpService/Program.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
config.SmtpTlsEnabled = tlsEnabled;
3535
builder.Services.AddSingleton(config);
3636

37-
builder.Services.AddAliasVaultSqliteConfiguration();
37+
builder.Services.AddAliasVaultDatabaseConfiguration(builder.Configuration);
3838
builder.Services.AddTransient<IMessageStore, DatabaseMessageStore>();
3939
builder.Services.AddSingleton(
4040
provider =>
@@ -112,7 +112,7 @@ static X509Certificate2 CreateCertificate()
112112
using (var scope = host.Services.CreateScope())
113113
{
114114
var container = scope.ServiceProvider;
115-
var factory = container.GetRequiredService<IDbContextFactory<AliasServerDbContext>>();
115+
var factory = container.GetRequiredService<IAliasServerDbContextFactory>();
116116
await using var context = await factory.CreateDbContextAsync();
117117
await context.Database.MigrateAsync();
118118
}

Diff for: src/Services/AliasVault.SmtpService/appsettings.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"Microsoft.EntityFrameworkCore": "Warning"
77
}
88
},
9+
"DatabaseProvider": "postgresql",
910
"ConnectionStrings": {
10-
"AliasServerDbContext": "Data Source=../../../database/AliasServerDb.sqlite"
11+
"AliasServerDbContext": "Host=localhost;Port=5432;Database=aliasvault;Username=aliasvault;Password=password"
1112
}
1213
}

Diff for: src/Services/AliasVault.TaskRunner/Program.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true);
2121
builder.Services.ConfigureLogging(builder.Configuration, Assembly.GetExecutingAssembly().GetName().Name!, "../../../logs");
2222

23-
builder.Services.AddAliasVaultSqliteConfiguration();
23+
builder.Services.AddAliasVaultDatabaseConfiguration(builder.Configuration);
2424

2525
// -----------------------------------------------------------------------
2626
// Register hosted services via Status library wrapper in order to monitor and control (start/stop) them via the database.
@@ -40,7 +40,7 @@
4040
using (var scope = host.Services.CreateScope())
4141
{
4242
var container = scope.ServiceProvider;
43-
var factory = container.GetRequiredService<IDbContextFactory<AliasServerDbContext>>();
43+
var factory = container.GetRequiredService<IAliasServerDbContextFactory>();
4444
await using var context = await factory.CreateDbContextAsync();
4545
await context.Database.MigrateAsync();
4646
}

Diff for: src/Services/AliasVault.TaskRunner/Tasks/EmailCleanupTask.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace AliasVault.TaskRunner.Tasks;
1717
public class EmailCleanupTask : IMaintenanceTask
1818
{
1919
private readonly ILogger<EmailCleanupTask> _logger;
20-
private readonly IDbContextFactory<AliasServerDbContext> _dbContextFactory;
20+
private readonly IAliasServerDbContextFactory _dbContextFactory;
2121
private readonly ServerSettingsService _settingsService;
2222

2323
/// <summary>
@@ -28,7 +28,7 @@ public class EmailCleanupTask : IMaintenanceTask
2828
/// <param name="settingsService">The settings service.</param>
2929
public EmailCleanupTask(
3030
ILogger<EmailCleanupTask> logger,
31-
IDbContextFactory<AliasServerDbContext> dbContextFactory,
31+
IAliasServerDbContextFactory dbContextFactory,
3232
ServerSettingsService settingsService)
3333
{
3434
_logger = logger;

Diff for: src/Services/AliasVault.TaskRunner/Tasks/EmailQuotaCleanupTask.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace AliasVault.TaskRunner.Tasks;
1717
public class EmailQuotaCleanupTask : IMaintenanceTask
1818
{
1919
private readonly ILogger<EmailQuotaCleanupTask> _logger;
20-
private readonly IDbContextFactory<AliasServerDbContext> _dbContextFactory;
20+
private readonly IAliasServerDbContextFactory _dbContextFactory;
2121
private readonly ServerSettingsService _settingsService;
2222

2323
/// <summary>
@@ -28,7 +28,7 @@ public class EmailQuotaCleanupTask : IMaintenanceTask
2828
/// <param name="settingsService">The settings service.</param>
2929
public EmailQuotaCleanupTask(
3030
ILogger<EmailQuotaCleanupTask> logger,
31-
IDbContextFactory<AliasServerDbContext> dbContextFactory,
31+
IAliasServerDbContextFactory dbContextFactory,
3232
ServerSettingsService settingsService)
3333
{
3434
_logger = logger;

Diff for: src/Services/AliasVault.TaskRunner/Tasks/LogCleanupTask.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace AliasVault.TaskRunner.Tasks;
1717
public class LogCleanupTask : IMaintenanceTask
1818
{
1919
private readonly ILogger<LogCleanupTask> _logger;
20-
private readonly IDbContextFactory<AliasServerDbContext> _dbContextFactory;
20+
private readonly IAliasServerDbContextFactory _dbContextFactory;
2121
private readonly ServerSettingsService _settingsService;
2222

2323
/// <summary>
@@ -28,7 +28,7 @@ public class LogCleanupTask : IMaintenanceTask
2828
/// <param name="settingsService">The settings service.</param>
2929
public LogCleanupTask(
3030
ILogger<LogCleanupTask> logger,
31-
IDbContextFactory<AliasServerDbContext> dbContextFactory,
31+
IAliasServerDbContextFactory dbContextFactory,
3232
ServerSettingsService settingsService)
3333
{
3434
_logger = logger;

Diff for: src/Services/AliasVault.TaskRunner/Tasks/RefreshTokenCleanupTask.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace AliasVault.TaskRunner.Tasks;
1616
public class RefreshTokenCleanupTask : IMaintenanceTask
1717
{
1818
private readonly ILogger<RefreshTokenCleanupTask> _logger;
19-
private readonly IDbContextFactory<AliasServerDbContext> _dbContextFactory;
19+
private readonly IAliasServerDbContextFactory _dbContextFactory;
2020

2121
/// <summary>
2222
/// Initializes a new instance of the <see cref="RefreshTokenCleanupTask"/> class.
@@ -25,7 +25,7 @@ public class RefreshTokenCleanupTask : IMaintenanceTask
2525
/// <param name="dbContextFactory">The database context factory.</param>
2626
public RefreshTokenCleanupTask(
2727
ILogger<RefreshTokenCleanupTask> logger,
28-
IDbContextFactory<AliasServerDbContext> dbContextFactory)
28+
IAliasServerDbContextFactory dbContextFactory)
2929
{
3030
_logger = logger;
3131
_dbContextFactory = dbContextFactory;

Diff for: src/Services/AliasVault.TaskRunner/Workers/TaskRunnerWorker.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class TaskRunnerWorker(
2424
ILogger<TaskRunnerWorker> logger,
2525
IEnumerable<IMaintenanceTask> tasks,
2626
ServerSettingsService settingsService,
27-
IDbContextFactory<AliasServerDbContext> dbContextFactory) : BackgroundService
27+
IAliasServerDbContextFactory dbContextFactory) : BackgroundService
2828
{
2929
/// <inheritdoc/>
3030
protected override async Task ExecuteAsync(CancellationToken stoppingToken)

Diff for: src/Services/AliasVault.TaskRunner/appsettings.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"Microsoft.EntityFrameworkCore": "Warning"
77
}
88
},
9+
"DatabaseProvider": "postgresql",
910
"ConnectionStrings": {
10-
"AliasServerDbContext": "Data Source=../../../database/AliasServerDb.sqlite"
11+
"AliasServerDbContext": "Host=localhost;Port=5432;Database=aliasvault;Username=aliasvault;Password=password"
1112
}
1213
}

Diff for: src/Utilities/AliasVault.WorkerStatus/Database/WorkerServiceStatus.cs

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ public class WorkerServiceStatus
2525
/// </summary>
2626
[Required]
2727
[StringLength(255)]
28-
[Column(TypeName = "varchar")]
2928
public string ServiceName { get; set; } = null!;
3029

3130
/// <summary>

Diff for: src/Utilities/AliasVault.WorkerStatus/ServiceExtensions/ServiceCollectionExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public static IServiceCollection AddStatusHostedService<TWorker, TContext>(this
3131
where TWorker : class, IHostedService
3232
where TContext : DbContext, IWorkerStatusDbContext
3333
{
34-
services.TryAddSingleton<TWorker>(); // Register the inner service
34+
services.TryAddSingleton<TWorker>();
3535
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, StatusHostedService<TWorker>>());
3636

3737
// Only add these required helper services if they are not already registered.

Diff for: src/Utilities/AliasVault.WorkerStatus/StatusWorker.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ private async Task<WorkerServiceStatus> GetServiceStatus()
9999
globalServiceStatus.Status = entry.CurrentStatus;
100100
globalServiceStatus.CurrentStatus = entry.CurrentStatus;
101101

102-
entry.Heartbeat = DateTime.Now;
102+
entry.Heartbeat = DateTime.UtcNow;
103103
await _dbContext.SaveChangesAsync();
104104

105105
return entry;
@@ -122,7 +122,7 @@ private async Task SetServiceStatus(WorkerServiceStatus statusEntry, string newS
122122
globalServiceStatus.Status = status;
123123
globalServiceStatus.CurrentStatus = status;
124124

125-
statusEntry.Heartbeat = DateTime.Now;
125+
statusEntry.Heartbeat = DateTime.UtcNow;
126126
await _dbContext.SaveChangesAsync();
127127
}
128128

0 commit comments

Comments
 (0)