// // Copyright (c) alveus.dev. All rights reserved. Licensed under the MIT License. // using Astral.Core.Exceptions; using Astral.Core.Infrastructure; using Dapper; using Injectio.Attributes; using Microsoft.Extensions.Logging; namespace Astral.DAL.Infrastructure; /// [RegisterScoped] public class DatabaseMigrator : IDatabaseMigrator { private readonly IDbConnectionProvider _dbConnectionProvider; private readonly ILogger _logger; private readonly ITransactionProvider _transactionProvider; /// /// Initializes a new instance of the class. /// /// Instance of . /// Instance of . /// Instance of . public DatabaseMigrator( IDbConnectionProvider dbConnectionProvider, ITransactionProvider transactionProvider, ILogger logger) { _dbConnectionProvider = dbConnectionProvider; _transactionProvider = transactionProvider; _logger = logger; } /// public async Task MigrateDatabaseAsync(string migrationsPath) { var connection = await _dbConnectionProvider.OpenConnectionAsync(); _logger.LogInformation("Beginning database migrations"); using var transaction = await _transactionProvider.BeginTransactionAsync(); await connection.ExecuteAsync(""" CREATE TABLE IF NOT EXISTS migrations ( date_added TIMESTAMP NOT NULL, filename VARCHAR(128) NOT NULL, CONSTRAINT migrations_filename_pk PRIMARY KEY (filename) ); """); var appliedMigrationsEnumerable = await connection.QueryAsync("SELECT filename FROM migrations ORDER BY date_added;"); // Prevent multiple enumeration. var appliedMigrations = appliedMigrationsEnumerable.ToList(); var files = Directory.GetFiles(migrationsPath, "*.sql").ToList(); files.Sort(); foreach (var filename in files) { var file = filename.Replace(migrationsPath, string.Empty) .Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); if (appliedMigrations.Contains(file)) { _logger.LogInformation("Already applied: {file}", file); continue; } _logger.LogInformation("Applying {file}...", file); try { var sql = await File.ReadAllTextAsync(filename); await connection.ExecuteAsync(sql); await connection.ExecuteAsync( "INSERT INTO migrations (date_added, filename) SELECT CURRENT_TIMESTAMP, @pFilename ;", new { pFilename = file }); } catch (Exception e) { throw new MigrationException(file, e.Message); } } transaction.Commit(); _logger.LogInformation("Database migrations complete"); } }