Files
meeting-assistant/MeetingAssistant/Speakers/SpeakerIdentitySchema.cs
T
2026-05-27 12:55:17 +02:00

123 lines
4.7 KiB
C#

using Microsoft.EntityFrameworkCore;
namespace MeetingAssistant.Speakers;
internal static class SpeakerIdentitySchema
{
public static async Task EnsureCreatedOrUpdatedAsync(
SpeakerIdentityDbContext context,
CancellationToken cancellationToken)
{
await context.Database.EnsureCreatedAsync(cancellationToken);
await EnsureSpeakerIdentityTimestampColumnsAsync(context, cancellationToken);
await context.Database.ExecuteSqlRawAsync(
"""
CREATE TABLE IF NOT EXISTS "SpeakerAliases" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_SpeakerAliases" PRIMARY KEY AUTOINCREMENT,
"SpeakerIdentityId" INTEGER NOT NULL,
"Name" TEXT NOT NULL,
CONSTRAINT "FK_SpeakerAliases_SpeakerIdentities_SpeakerIdentityId"
FOREIGN KEY ("SpeakerIdentityId") REFERENCES "SpeakerIdentities" ("Id") ON DELETE CASCADE
);
""",
cancellationToken);
await context.Database.ExecuteSqlRawAsync(
"""
CREATE UNIQUE INDEX IF NOT EXISTS "IX_SpeakerAliases_SpeakerIdentityId_Name"
ON "SpeakerAliases" ("SpeakerIdentityId", "Name");
""",
cancellationToken);
await context.Database.ExecuteSqlRawAsync(
"""
CREATE TABLE IF NOT EXISTS "SpeakerIdentityReferences" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_SpeakerIdentityReferences" PRIMARY KEY AUTOINCREMENT,
"SpeakerIdentityId" INTEGER NOT NULL,
"MeetingNotePath" TEXT NOT NULL,
"TranscriptPath" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
CONSTRAINT "FK_SpeakerIdentityReferences_SpeakerIdentities_SpeakerIdentityId"
FOREIGN KEY ("SpeakerIdentityId") REFERENCES "SpeakerIdentities" ("Id") ON DELETE CASCADE
);
""",
cancellationToken);
await context.Database.ExecuteSqlRawAsync(
"""
CREATE UNIQUE INDEX IF NOT EXISTS "IX_SpeakerIdentityReferences_SpeakerIdentityId_MeetingNotePath_TranscriptPath"
ON "SpeakerIdentityReferences" ("SpeakerIdentityId", "MeetingNotePath", "TranscriptPath");
""",
cancellationToken);
}
private static async Task EnsureSpeakerIdentityTimestampColumnsAsync(
SpeakerIdentityDbContext context,
CancellationToken cancellationToken)
{
const string defaultTimestamp = "1970-01-01T00:00:00.0000000+00:00";
var hasCreatedAt = await HasColumnAsync(context, "SpeakerIdentities", "CreatedAt", cancellationToken);
if (!hasCreatedAt)
{
await context.Database.ExecuteSqlRawAsync(
$"""
ALTER TABLE "SpeakerIdentities"
ADD COLUMN "CreatedAt" TEXT NOT NULL DEFAULT '{defaultTimestamp}';
""",
cancellationToken);
}
var hasUpdatedAt = await HasColumnAsync(context, "SpeakerIdentities", "UpdatedAt", cancellationToken);
if (!hasUpdatedAt)
{
await context.Database.ExecuteSqlRawAsync(
$"""
ALTER TABLE "SpeakerIdentities"
ADD COLUMN "UpdatedAt" TEXT NOT NULL DEFAULT '{defaultTimestamp}';
""",
cancellationToken);
await context.Database.ExecuteSqlRawAsync(
$"""
UPDATE "SpeakerIdentities"
SET "UpdatedAt" = "CreatedAt"
WHERE "UpdatedAt" = '{defaultTimestamp}';
""",
cancellationToken);
}
}
private static async Task<bool> HasColumnAsync(
SpeakerIdentityDbContext context,
string tableName,
string columnName,
CancellationToken cancellationToken)
{
var connection = context.Database.GetDbConnection();
var shouldClose = connection.State == System.Data.ConnectionState.Closed;
if (shouldClose)
{
await connection.OpenAsync(cancellationToken);
}
try
{
await using var command = connection.CreateCommand();
command.CommandText = $"""PRAGMA table_info("{tableName}")""";
await using var reader = await command.ExecuteReaderAsync(cancellationToken);
while (await reader.ReadAsync(cancellationToken))
{
if (string.Equals(reader.GetString(1), columnName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
finally
{
if (shouldClose)
{
await connection.CloseAsync();
}
}
}
}