using Dapper;
using MySqlConnector;
using IBKRTrader.Core.Logging;
using IBKRTrader.Core.Settings;
namespace IBKRTrader.Core.Database;
///
/// Zentraler Datenbankzugriff via MySqlConnector + Dapper.
/// Jede Methode öffnet eine eigene Connection (Connection-Pooling via MySqlConnector).
///
public class DatabaseService
{
private readonly SettingsService _settings;
private readonly LoggingService _logger;
public DatabaseService(SettingsService settings, LoggingService logger)
{
_settings = settings;
_logger = logger;
// Dapper support for DateOnly
SqlMapper.AddTypeHandler(new DateOnlyTypeHandler());
}
private class DateOnlyTypeHandler : SqlMapper.TypeHandler
{
public override void SetValue(System.Data.IDbDataParameter parameter, DateOnly value)
{
parameter.Value = value.ToString("yyyy-MM-dd");
}
public override DateOnly Parse(object value)
{
if (value is DateTime dt) return DateOnly.FromDateTime(dt);
if (value is string s && DateOnly.TryParse(s, out var d)) return d;
return DateOnly.FromDateTime(Convert.ToDateTime(value));
}
}
// ─── Connection ───────────────────────────────────────────────────────────
public MySqlConnection CreateConnection()
=> new(_settings.Settings.Database.BuildConnectionString());
public async Task TestConnectionAsync()
{
try
{
await using var conn = CreateConnection();
await conn.OpenAsync();
_logger.Info("Core", "Datenbankverbindung erfolgreich hergestellt.");
return true;
}
catch (Exception ex)
{
_logger.Error("Core", "Datenbankverbindung fehlgeschlagen.", ex);
return false;
}
}
// ─── Abfragen (Dapper) ────────────────────────────────────────────────────
public async Task> QueryAsync(string sql, object? param = null)
{
await using var conn = CreateConnection();
return await conn.QueryAsync(sql, param);
}
public async Task QueryFirstOrDefaultAsync(string sql, object? param = null)
{
await using var conn = CreateConnection();
return await conn.QueryFirstOrDefaultAsync(sql, param);
}
public async Task ExecuteAsync(string sql, object? param = null)
{
await using var conn = CreateConnection();
return await conn.ExecuteAsync(sql, param);
}
public async Task ExecuteScalarAsync(string sql, object? param = null)
{
await using var conn = CreateConnection();
#pragma warning disable CS8603
return await conn.ExecuteScalarAsync(sql, param);
#pragma warning restore CS8603
}
// ─── Worker-Log Hilfsmethoden ─────────────────────────────────────────────
/// Legt einen neuen Worker-Log-Eintrag an und gibt die neue ID zurück.
public async Task BeginWorkerLogAsync(string workerName, string module)
{
const string sql =
@"INSERT INTO `core_worker_log` (worker_name, module, started_at, status)
VALUES (@workerName, @module, @now, 'Running');
SELECT LAST_INSERT_ID();";
return await ExecuteScalarAsync(sql,
new { workerName, module, now = DateTime.UtcNow });
}
/// Schließt einen Worker-Log-Eintrag ab (Success oder Error).
public async Task EndWorkerLogAsync(long logId, bool success, string? message = null)
{
const string sql =
@"UPDATE `core_worker_log`
SET finished_at = @now, status = @status, message = @msg
WHERE id = @id";
await ExecuteAsync(sql, new
{
id = logId,
now = DateTime.UtcNow,
status = success ? "Success" : "Error",
msg = message
});
}
}