using System.Data; using System.Text; using Dapper; namespace MesaFabApproval.API.Services; public interface IDalService { Task> QueryAsync(string sql); Task> QueryAsync(string sql, object paramaters); Task ExecuteAsync(string sql); Task ExecuteAsync(string sql, T paramaters); } public class DalService : IDalService { private static readonly int RETRIES = 3; private static readonly int BACKOFF_SECONDS_INTERVAL = 30; private readonly ILogger _logger; private readonly IDbConnectionService _dbConnectionService; public DalService(IDbConnectionService dbConnectionService, ILogger logger) { _dbConnectionService = dbConnectionService ?? throw new ArgumentNullException("IDbConnectionService not injected"); _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); } public async Task> QueryAsync(string sql) { if (sql is null) throw new ArgumentNullException("sql cannot be null"); int remainingRetries = RETRIES; bool queryWasSuccessful = false; Exception exception = null; IEnumerable result = new List(); while (!queryWasSuccessful && remainingRetries > 0) { int backoffSeconds = (RETRIES - remainingRetries--) * BACKOFF_SECONDS_INTERVAL; Task.Delay(backoffSeconds * 1000).Wait(); try { _logger.LogInformation($"Attempting to perform query with {sql}. Remaining retries: {remainingRetries}"); using (IDbConnection conn = _dbConnectionService.GetConnection()) { result = await conn.QueryAsync(sql); } queryWasSuccessful = true; } catch (Exception ex) { _logger.LogError($"An exception occurred while attempting to perform a query. Exception: {ex.Message}"); exception = ex; } } if (!queryWasSuccessful && exception is not null) { throw exception; } return result; } public async Task> QueryAsync(string sql, object parameters) { if (sql is null) throw new ArgumentNullException("sql cannot be null"); if (parameters is null) throw new ArgumentNullException("parameters cannot be null"); StringBuilder logBuilder = new(); int remainingRetries = RETRIES; bool queryWasSuccessful = false; Exception exception = null; IEnumerable result = new List(); while (!queryWasSuccessful && remainingRetries > 0) { int backoffSeconds = (RETRIES - remainingRetries--) * BACKOFF_SECONDS_INTERVAL; Task.Delay(backoffSeconds * 1000).Wait(); try { logBuilder.Clear(); logBuilder.Append($"Attempting to perform query with {sql} "); logBuilder.Append($"and parameters {parameters.ToString()}. "); logBuilder.Append($"Remaining retries: {remainingRetries}"); _logger.LogInformation(logBuilder.ToString()); using (IDbConnection conn = _dbConnectionService.GetConnection()) { result = await conn.QueryAsync(sql, parameters); } queryWasSuccessful = true; } catch (Exception ex) { _logger.LogError($"An exception occurred while attempting to perform a query. Exception: {ex.Message}"); exception = ex; } } if (!queryWasSuccessful && exception is not null) { throw exception; } return result; } public async Task ExecuteAsync(string sql) { if (sql is null) throw new ArgumentNullException("sql cannot be null"); int remainingRetries = RETRIES; bool queryWasSuccessful = false; Exception exception = null; int rowsAffected = 0; while (!queryWasSuccessful && remainingRetries > 0) { int backoffSeconds = (RETRIES - remainingRetries--) * BACKOFF_SECONDS_INTERVAL; Task.Delay(backoffSeconds * 1000).Wait(); try { _logger.LogInformation($"Attempting to execute {sql}. Remaining retries: {remainingRetries}"); using (IDbConnection conn = _dbConnectionService.GetConnection()) { rowsAffected = await conn.ExecuteAsync(sql); } queryWasSuccessful = true; } catch (Exception ex) { _logger.LogError($"An exception occurred while attempting to execute a query. Exception: {ex.Message}"); exception = ex; } } if (!queryWasSuccessful && exception is not null) { throw exception; } return rowsAffected; } public async Task ExecuteAsync(string sql, T parameters) { if (sql is null) throw new ArgumentNullException("sql cannot be null"); int remainingRetries = RETRIES; bool queryWasSuccessful = false; Exception exception = null; int rowsAffected = 0; while (!queryWasSuccessful && remainingRetries > 0) { int backoffSeconds = (RETRIES - remainingRetries--) * BACKOFF_SECONDS_INTERVAL; Task.Delay(backoffSeconds * 1000).Wait(); try { _logger.LogInformation($"Attempting to execute {sql} with parameters. Remaining retries: {remainingRetries}"); using (IDbConnection conn = _dbConnectionService.GetConnection()) { rowsAffected = await conn.ExecuteAsync(sql, parameters); } queryWasSuccessful = true; } catch (Exception ex) { _logger.LogError($"An exception occurred while attempting to execute a query. Exception: {ex.Message}"); exception = ex; } } if (!queryWasSuccessful && exception is not null) { throw exception; } return rowsAffected; } }