galaeth-draft/Galaeth.DAL/Infrastructure/DatabaseMigrator.cs
2024-11-17 09:31:01 +00:00

91 lines
3.3 KiB
C#

using Dapper;
using Galaeth.Core.Exceptions;
using Galaeth.Core.Infrastructure;
using Injectio.Attributes;
using Microsoft.Extensions.Logging;
namespace Galaeth.DAL.Infrastructure;
/// <inheritdoc />
[RegisterScoped]
public class DatabaseMigrator : IDatabaseMigrator
{
private readonly IDbConnectionProvider _dbConnectionProvider;
private readonly ITransactionProvider _transactionProvider;
private readonly ILogger<DatabaseMigrator> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="DatabaseMigrator"/> class.
/// </summary>
/// <param name="dbConnectionProvider">Instance of <see cref="IDbConnectionProvider"/>.</param>
/// <param name="transactionProvider">Instance of <see cref="ITransactionProvider"/>.</param>
/// <param name="logger">Instance of <see cref="ILogger"/>.</param>
public DatabaseMigrator(
IDbConnectionProvider dbConnectionProvider,
ITransactionProvider transactionProvider,
ILogger<DatabaseMigrator> logger)
{
_dbConnectionProvider = dbConnectionProvider;
_transactionProvider = transactionProvider;
_logger = logger;
}
/// <inheritdoc />
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<string>("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");
}
}