Switches the database from SQL Server to MariaDB

The target Pi only has MariaDB, and SQL Server has no ARM64 build at
all - not a config problem, a real gap discovered during deployment
setup. Swapped the EF Core provider, regenerated every migration,
updated connection strings and the backup script everywhere they
appear.

Took two tries to land on a provider that actually works: Pomelo
builds fine against this project's EF Core 10 packages but fails at
runtime (it's compiled against 9's internal API surface, which moved
in 10 wherever Identity/DataProtection force the newer packages).
Oracle's official provider builds and migrates fine but has a real
MariaDB bug in its own migration-lock code, reproduced against a live
database. Kept Oracle's provider and worked around just that one
broken method - everything else it does is correct - rather than
give up more of the stack to chase a workaround.

Verified against a real local MariaDB end to end: all three
migrations applied, both hosts start clean, full suite still green.
This commit is contained in:
2026-07-29 11:59:09 +02:00
parent 579e0ceaac
commit 33d18ebbf8
33 changed files with 575 additions and 1367 deletions
@@ -1,10 +1,12 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http.Resilience;
using Polly;
using SlpModularCms.Core.Hosting;
using SlpModularCms.Core.Modules;
using SlpModularCms.Modules.Master.BackgroundServices;
using SlpModularCms.Modules.Master.Data;
@@ -33,7 +35,8 @@ public class MasterModule : IModule
services.AddDbContext<MasterDbContext>((serviceProvider, options) =>
{
var config = serviceProvider.GetRequiredService<IConfiguration>();
options.UseSqlServer(config.GetConnectionString("DefaultConnection"));
options.UseMySQL(config.GetConnectionString("DefaultConnection")!);
options.ReplaceService<IHistoryRepository, NonLockingMySQLHistoryRepository>();
});
services.AddScoped<ICmsInstanceRepository, CmsInstanceRepository>();
@@ -2,7 +2,6 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SlpModularCms.Modules.Master.Data;
@@ -12,7 +11,7 @@ using SlpModularCms.Modules.Master.Data;
namespace SlpModularCms.Modules.Master.Migrations
{
[DbContext(typeof(MasterDbContext))]
[Migration("20260630210103_InitialCreate")]
[Migration("20260729095359_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
@@ -21,38 +20,36 @@ namespace SlpModularCms.Modules.Master.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("SlpModularCms.Modules.Master.Data.Entities.CmsInstance", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
.HasColumnType("char(36)");
b.Property<string>("ApiKey")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
.HasColumnType("varchar(1000)");
b.Property<string>("DisableMessage")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
.HasColumnType("varchar(500)");
b.Property<DateTimeOffset?>("LastContactedAt")
.HasColumnType("datetimeoffset");
.HasColumnType("datetime");
b.Property<DateTimeOffset?>("LastIntegrityCheckFailedAt")
.HasColumnType("datetimeoffset");
.HasColumnType("datetime");
b.Property<DateTimeOffset?>("LastStatusPushedAt")
.HasColumnType("datetimeoffset");
.HasColumnType("datetime");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
.HasColumnType("varchar(200)");
b.Property<int>("Status")
.HasColumnType("int");
@@ -60,7 +57,7 @@ namespace SlpModularCms.Modules.Master.Migrations
b.Property<string>("Url")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
.HasColumnType("varchar(500)");
b.HasKey("Id");
@@ -11,24 +11,28 @@ namespace SlpModularCms.Modules.Master.Migrations
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterDatabase()
.Annotation("MySQL:Charset", "utf8mb4");
migrationBuilder.CreateTable(
name: "MasterCmsInstances",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
Url = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false),
ApiKey = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
Id = table.Column<Guid>(type: "char(36)", nullable: false),
Name = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: false),
Url = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: false),
ApiKey = table.Column<string>(type: "varchar(1000)", maxLength: 1000, nullable: false),
Status = table.Column<int>(type: "int", nullable: false),
DisableMessage = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
LastContactedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
LastStatusPushedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
LastIntegrityCheckFailedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true)
DisableMessage = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: true),
LastContactedAt = table.Column<DateTimeOffset>(type: "datetime", nullable: true),
LastStatusPushedAt = table.Column<DateTimeOffset>(type: "datetime", nullable: true),
LastIntegrityCheckFailedAt = table.Column<DateTimeOffset>(type: "datetime", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_MasterCmsInstances", x => x.Id);
});
})
.Annotation("MySQL:Charset", "utf8mb4");
}
/// <inheritdoc />
@@ -2,7 +2,6 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SlpModularCms.Modules.Master.Data;
@@ -18,38 +17,36 @@ namespace SlpModularCms.Modules.Master.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("SlpModularCms.Modules.Master.Data.Entities.CmsInstance", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
.HasColumnType("char(36)");
b.Property<string>("ApiKey")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
.HasColumnType("varchar(1000)");
b.Property<string>("DisableMessage")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
.HasColumnType("varchar(500)");
b.Property<DateTimeOffset?>("LastContactedAt")
.HasColumnType("datetimeoffset");
.HasColumnType("datetime");
b.Property<DateTimeOffset?>("LastIntegrityCheckFailedAt")
.HasColumnType("datetimeoffset");
.HasColumnType("datetime");
b.Property<DateTimeOffset?>("LastStatusPushedAt")
.HasColumnType("datetimeoffset");
.HasColumnType("datetime");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
.HasColumnType("varchar(200)");
b.Property<int>("Status")
.HasColumnType("int");
@@ -57,7 +54,7 @@ namespace SlpModularCms.Modules.Master.Migrations
b.Property<string>("Url")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
.HasColumnType("varchar(500)");
b.HasKey("Id");