Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/SharpFunctional.MSSQL.Example/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
await using var sqlConnection = new SqlConnection(connectionString);
await sqlConnection.OpenAsync();

var db = new FunctionalMsSqlDb(dbContext: dbContext, connection: sqlConnection);
var db = new FunctionalMsSqlDb(dbContext: dbContext, dbConnection: sqlConnection);

// ═══════════════════════════════════════════════════════════════════════════
// 2. Seed data — customers, products, orders, and order lines
Expand Down
17 changes: 9 additions & 8 deletions src/SharpFunctional.MSSQL/Common/CircuitBreaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,15 @@ public sealed class CircuitBreakerOptions
/// }, cancellationToken);
/// </code>
/// </example>
public sealed class CircuitBreaker(CircuitBreakerOptions? options = null)
public sealed class CircuitBreaker(CircuitBreakerOptions? options = null, TimeProvider? timeProvider = null)
{
private readonly CircuitBreakerOptions _options = options ?? new CircuitBreakerOptions();
private readonly TimeProvider _timeProvider = timeProvider ?? TimeProvider.System;
private readonly Lock _stateLock = new();

private CircuitState _state = CircuitState.Closed;
private DateTime _openedAtUtc = DateTime.MinValue;
private DateTime _stateChangedAtUtc = DateTime.UtcNow;
private DateTime _stateChangedAtUtc = (timeProvider ?? TimeProvider.System).GetUtcNow().UtcDateTime;
private int _failureCount;
private int _halfOpenSuccessCount;

Expand Down Expand Up @@ -120,7 +121,7 @@ public CircuitBreakerSnapshot GetSnapshot()
{
lock (_stateLock)
{
var nowUtc = DateTime.UtcNow;
var nowUtc = _timeProvider.GetUtcNow().UtcDateTime;
var state = EvaluateState(nowUtc);
return new CircuitBreakerSnapshot(
State: state,
Expand Down Expand Up @@ -186,12 +187,12 @@ public void Reset()
_state = CircuitState.Closed;
_failureCount = 0;
_halfOpenSuccessCount = 0;
_stateChangedAtUtc = DateTime.UtcNow;
_stateChangedAtUtc = _timeProvider.GetUtcNow().UtcDateTime;
}
}

/// <remarks>Must be called inside <c>lock (_stateLock)</c>.</remarks>
private CircuitState EvaluateState() => EvaluateState(DateTime.UtcNow);
private CircuitState EvaluateState() => EvaluateState(_timeProvider.GetUtcNow().UtcDateTime);

/// <remarks>Must be called inside <c>lock (_stateLock)</c>.</remarks>
private CircuitState EvaluateState(DateTime utcNow)
Expand All @@ -215,13 +216,13 @@ private void RecordFailure()
if (_state == CircuitState.HalfOpen)
{
_state = CircuitState.Open;
_openedAtUtc = DateTime.UtcNow;
_openedAtUtc = _timeProvider.GetUtcNow().UtcDateTime;
_stateChangedAtUtc = _openedAtUtc;
}
else if (_state == CircuitState.Closed && _failureCount >= _options.FailureThreshold)
{
_state = CircuitState.Open;
_openedAtUtc = DateTime.UtcNow;
_openedAtUtc = _timeProvider.GetUtcNow().UtcDateTime;
_stateChangedAtUtc = _openedAtUtc;
}
}
Expand All @@ -238,7 +239,7 @@ private void RecordSuccess()
_state = CircuitState.Closed;
_failureCount = 0;
_halfOpenSuccessCount = 0;
_stateChangedAtUtc = DateTime.UtcNow;
_stateChangedAtUtc = _timeProvider.GetUtcNow().UtcDateTime;
}
}
else
Expand Down
21 changes: 18 additions & 3 deletions src/SharpFunctional.MSSQL/Common/FunctionalExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,13 @@ public static async Task<Option<TOut>> Bind<TIn, TOut>(
None: () => Task.FromResult(Option<TOut>.None))
.ConfigureAwait(false);
}
catch
catch (OperationCanceledException)
{
throw;
}
catch (Exception)
{
// TODO -review This eats all exception information. Consider logging the exception or returning a Result type that can capture it. Or leave it to the caller to log by throwing.
return Option<TOut>.None;
}
}
Expand Down Expand Up @@ -66,8 +71,13 @@ public static async Task<Seq<TOut>> Bind<TIn, TOut>(
None: () => Task.FromResult(Seq<TOut>()))
.ConfigureAwait(false);
}
catch
catch (OperationCanceledException)
{
throw;
}
catch (Exception)
{
// TODO -review This eats all exception information. Consider logging the exception or returning a Result type that can capture it. Or leave it to the caller to log by throwing.
return Seq<TOut>();
}
}
Expand Down Expand Up @@ -95,8 +105,13 @@ public static async Task<Seq<TOut>> Map<TIn, TOut>(
var sequence = await source.WaitAsync(cancellationToken).ConfigureAwait(false);
return mapper(sequence);
}
catch
catch (OperationCanceledException)
{
throw;
}
catch (Exception)
{
// TODO -review This eats all exception information. Consider logging the exception or returning a Result type that can capture it. Or leave it to the caller to log by throwing.
return Seq<TOut>();
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/SharpFunctional.MSSQL/Dapper/DapperFunctionalDb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public async Task<Option<T>> ExecuteStoredProcSingleAsync<T>(
var command = new CommandDefinition(
procName,
param,
transaction: Owner.AmbientTransaction,
transaction: Owner.GetAmbientTransaction(),
commandTimeout: Options.CommandTimeoutSeconds,
commandType: CommandType.StoredProcedure,
cancellationToken: ct);
Expand Down Expand Up @@ -112,7 +112,7 @@ public async Task<Seq<T>> ExecuteStoredProcAsync<T>(
var command = new CommandDefinition(
procName,
param,
transaction: Owner.AmbientTransaction,
transaction: Owner.GetAmbientTransaction(),
commandTimeout: Options.CommandTimeoutSeconds,
commandType: CommandType.StoredProcedure,
cancellationToken: ct);
Expand Down Expand Up @@ -173,7 +173,7 @@ await ExecuteWithRetryAsync(
var command = new CommandDefinition(
procName,
param,
transaction: Owner.AmbientTransaction,
transaction: Owner.GetAmbientTransaction(),
commandTimeout: Options.CommandTimeoutSeconds,
commandType: CommandType.StoredProcedure,
cancellationToken: ct);
Expand Down Expand Up @@ -231,7 +231,7 @@ public async Task<Seq<T>> QueryAsync<T>(
var command = new CommandDefinition(
sql,
param,
transaction: Owner.AmbientTransaction,
transaction: Owner.GetAmbientTransaction(),
commandTimeout: Options.CommandTimeoutSeconds,
cancellationToken: ct);

Expand Down Expand Up @@ -287,7 +287,7 @@ public async Task<Option<T>> QuerySingleAsync<T>(
var command = new CommandDefinition(
sql,
param,
transaction: Owner.AmbientTransaction,
transaction: Owner.GetAmbientTransaction(),
commandTimeout: Options.CommandTimeoutSeconds,
cancellationToken: ct);

Expand Down Expand Up @@ -358,7 +358,7 @@ public async Task<Fin<QueryResults<T>>> ExecuteStoredProcPaginatedAsync<T>(
var command = new CommandDefinition(
procName,
param,
transaction: Owner.AmbientTransaction,
transaction: Owner.GetAmbientTransaction(),
commandTimeout: Options.CommandTimeoutSeconds,
commandType: CommandType.StoredProcedure,
cancellationToken: ct);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public static IServiceCollection AddFunctionalMsSqlDapper(
var opts = sp.GetRequiredService<IOptions<FunctionalMsSqlDbOptions>>().Value;
var connection = new SqlConnection(opts.ConnectionString);
var logger = sp.GetService<ILogger<FunctionalMsSqlDb>>();
return new FunctionalMsSqlDb(connection: connection, executionOptions: opts.ExecutionOptions, logger: logger);
return new FunctionalMsSqlDb(dbConnection: connection, executionOptions: opts.ExecutionOptions, logger: logger);
});

return services;
Expand Down Expand Up @@ -132,7 +132,7 @@ public static IServiceCollection AddFunctionalMsSql<TContext>(
var opts = sp.GetRequiredService<IOptions<FunctionalMsSqlDbOptions>>().Value;
var connection = new SqlConnection(opts.ConnectionString);
var logger = sp.GetService<ILogger<FunctionalMsSqlDb>>();
return new FunctionalMsSqlDb(dbContext: context, connection: connection, executionOptions: opts.ExecutionOptions, logger: logger);
return new FunctionalMsSqlDb(dbContext: context, dbConnection: connection, executionOptions: opts.ExecutionOptions, logger: logger);
});

return services;
Expand Down Expand Up @@ -180,7 +180,7 @@ public static IServiceCollection AddFunctionalMsSql<TContext>(

var connection = new SqlConnection(opts.ConnectionString);
var logger = sp.GetService<ILogger<FunctionalMsSqlDb>>();
return new FunctionalMsSqlDb(dbContext: context, connection: connection, executionOptions: opts.ExecutionOptions, logger: logger);
return new FunctionalMsSqlDb(dbContext: context, dbConnection: connection, executionOptions: opts.ExecutionOptions, logger: logger);
});

return services;
Expand Down
22 changes: 11 additions & 11 deletions src/SharpFunctional.MSSQL/FunctionalMsSqlDb.Log.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,35 @@ namespace SharpFunctional.MsSql;
internal static partial class FunctionalMsSqlDbLog
{
[LoggerMessage(EventId = 1000, Level = LogLevel.Debug, Message = "Starting EF transaction for result type {ResultType}")]
internal static partial void StartingEfTransaction(ILogger logger, string resultType);
internal static partial void StartingEfTransaction(this ILogger logger, string resultType);

[LoggerMessage(EventId = 1001, Level = LogLevel.Debug, Message = "Committed EF transaction for result type {ResultType}")]
internal static partial void CommittedEfTransaction(ILogger logger, string resultType);
internal static partial void CommittedEfTransaction(this ILogger logger, string resultType);

[LoggerMessage(EventId = 1002, Level = LogLevel.Warning, Message = "Rolled back EF transaction due to failed result for type {ResultType}")]
internal static partial void RolledBackEfTransaction(ILogger logger, string resultType);
internal static partial void RolledBackEfTransaction(this ILogger logger, string resultType);

[LoggerMessage(EventId = 1003, Level = LogLevel.Error, Message = "EF transaction failed for result type {ResultType}")]
internal static partial void EfTransactionFailed(ILogger logger, string resultType, Exception exception);
internal static partial void EfTransactionFailed(this ILogger logger, string resultType, Exception exception);

[LoggerMessage(EventId = 1010, Level = LogLevel.Debug, Message = "Starting Dapper transaction for result type {ResultType}")]
internal static partial void StartingDapperTransaction(ILogger logger, string resultType);
internal static partial void StartingDapperTransaction(this ILogger logger, string resultType);

[LoggerMessage(EventId = 1011, Level = LogLevel.Debug, Message = "Committed Dapper transaction for result type {ResultType}")]
internal static partial void CommittedDapperTransaction(ILogger logger, string resultType);
internal static partial void CommittedDapperTransaction(this ILogger logger, string resultType);

[LoggerMessage(EventId = 1012, Level = LogLevel.Warning, Message = "Rolled back Dapper transaction due to failed result for type {ResultType}")]
internal static partial void RolledBackDapperTransaction(ILogger logger, string resultType);
internal static partial void RolledBackDapperTransaction(this ILogger logger, string resultType);

[LoggerMessage(EventId = 1013, Level = LogLevel.Error, Message = "Dapper transaction failed for result type {ResultType}")]
internal static partial void DapperTransactionFailed(ILogger logger, string resultType, Exception exception);
internal static partial void DapperTransactionFailed(this ILogger logger, string resultType, Exception exception);

[LoggerMessage(EventId = 1020, Level = LogLevel.Debug, Message = "Opened SQL connection after {AttemptCount} attempt(s)")]
internal static partial void OpenedSqlConnection(ILogger logger, int attemptCount);
internal static partial void OpenedSqlConnection(this ILogger logger, int attemptCount);

[LoggerMessage(EventId = 1021, Level = LogLevel.Warning, Message = "Transient SQL open failure on attempt {Attempt}. Retrying in {DelayMs} ms")]
internal static partial void TransientSqlOpenFailure(ILogger logger, int attempt, double delayMs, Exception exception);
internal static partial void TransientSqlOpenFailure(this ILogger logger, int attempt, double delayMs, Exception exception);

[LoggerMessage(EventId = 1022, Level = LogLevel.Error, Message = "SQL connection open failed after {AttemptCount} attempt(s)")]
internal static partial void SqlConnectionOpenFailed(ILogger logger, int attemptCount, Exception exception);
internal static partial void SqlConnectionOpenFailed(this ILogger logger, int attemptCount, Exception exception);
}
Loading
Loading