Phase 1 WP-1: EF Core DbContext with Fluent API mappings for all 26 entities

ScadaLinkDbContext with 10 configuration classes (Fluent API only), initial
migration creating 25 tables, environment-aware migration helper (auto-apply
dev, validate-only prod), DesignTimeDbContextFactory, optimistic concurrency
on DeploymentRecord. 20 tests verify schema, CRUD, relationships, cascades.
This commit is contained in:
Joseph Doherty
2026-03-16 19:15:50 -04:00
parent 9bc5a5163f
commit 1996b21961
23 changed files with 4494 additions and 9 deletions

View File

@@ -0,0 +1,41 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace ScadaLink.ConfigurationDatabase;
/// <summary>
/// Provides environment-aware migration behavior for the ScadaLink configuration database.
/// </summary>
public static class MigrationHelper
{
/// <summary>
/// Applies pending migrations (development mode) or validates schema version (production mode).
/// </summary>
/// <param name="dbContext">The database context to migrate or validate.</param>
/// <param name="isDevelopment">When true, auto-applies migrations. When false, validates schema version matches.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public static async Task ApplyOrValidateMigrationsAsync(
ScadaLinkDbContext dbContext,
bool isDevelopment,
CancellationToken cancellationToken = default)
{
if (isDevelopment)
{
await dbContext.Database.MigrateAsync(cancellationToken);
}
else
{
var pendingMigrations = await dbContext.Database.GetPendingMigrationsAsync(cancellationToken);
var pending = pendingMigrations.ToList();
if (pending.Count > 0)
{
throw new InvalidOperationException(
$"Database schema is out of date. {pending.Count} pending migration(s): {string.Join(", ", pending)}. " +
"Apply migrations using 'dotnet ef database update' or the generated SQL scripts before starting in production mode.");
}
}
}
}