49 lines
2.2 KiB
C#
49 lines
2.2 KiB
C#
using Npgsql;
|
||
using Server.WatchBody.Core.Dal.Src.Repository;
|
||
using Server.WatchBody.Web.Db;
|
||
|
||
namespace Server.WatchBody.Web.Services;
|
||
|
||
/// <summary>
|
||
/// Режим --mode migrate (З-36, requirements 2.3): применение файлов
|
||
/// 2-db\migrations\ по возрастанию имён; файл не в журнале src.migration
|
||
/// — выполняется целиком в транзакции (скрипт + запись журнала), сбой —
|
||
/// откат и код возврата 1. Миграции идемпотентны (IF NOT EXISTS /
|
||
/// OR REPLACE) — повторный запуск безопасен.
|
||
/// </summary>
|
||
public class MigrationRunner(PostgresConnectionString connectionString)
|
||
{
|
||
public int Apply(string migrationsDir)
|
||
{
|
||
if (!Directory.Exists(migrationsDir))
|
||
throw new DirectoryNotFoundException($"каталог миграций не найден: {migrationsDir}");
|
||
var repository = new MigrationRepository(connectionString);
|
||
var applied = 0;
|
||
foreach (var file in Directory.GetFiles(migrationsDir, "*.sql")
|
||
.OrderBy(f => Path.GetFileName(f), StringComparer.Ordinal))
|
||
{
|
||
var name = Path.GetFileName(file);
|
||
if (repository.Select(name) != null) continue;
|
||
|
||
var sql = File.ReadAllText(file);
|
||
using (var scope = new System.Transactions.TransactionScope(
|
||
System.Transactions.TransactionScopeAsyncFlowOption.Enabled))
|
||
{
|
||
// скрипт и журнал — одна ambient-транзакция (оба соединения
|
||
// Npgsql, Enlist по умолчанию); сбой → откат всего файла
|
||
using (var connection = new NpgsqlConnection(connectionString.Value))
|
||
{
|
||
connection.Open();
|
||
using var command = new NpgsqlCommand(sql, connection);
|
||
command.ExecuteNonQuery();
|
||
}
|
||
repository.Insert(name);
|
||
scope.Complete();
|
||
}
|
||
Console.WriteLine($"миграция применена: {name}");
|
||
applied++;
|
||
}
|
||
return applied;
|
||
}
|
||
}
|