Skip to content

Commit

Permalink
Upgrade to C# 12.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dixin committed Jan 27, 2024
1 parent 69d7cf5 commit 0b96fb6
Show file tree
Hide file tree
Showing 23 changed files with 143 additions and 285 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
/// Represents the options for <see cref="RetryManager"/>.
/// </summary>
public record RetryManagerOptions(
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
string? DefaultRetryStrategy,
string? DefaultSqlConnectionRetryStrategy,
string? DefaultSqlCommandRetryStrategy,
string? DefaultAzureServiceBusRetryStrategy,
string? DefaultAzureCachingRetryStrategy,
string? DefaultAzureStorageRetryStrategy,
IConfigurationSection? RetryStrategy)
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
/// <summary>
/// Initializes a new instance of the <see cref="RetryManagerOptions" /> record.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,145 +3,88 @@
/// <summary>
/// Represents the options for <see cref="Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryStrategy" />.
/// </summary>
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public abstract record RetryStrategyOptions(bool FastFirstRetry);
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member

/// <summary>
/// Represents the options for <see cref="Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.FixedInterval" /> retry strategy.
/// </summary>
public record FixedIntervalOptions(bool FastFirstRetry, int RetryCount, TimeSpan RetryInterval) : RetryStrategyOptions(FastFirstRetry)
{
private readonly int retryCount;

private readonly TimeSpan retryInterval;

/// <summary>
/// Initializes a new instance of the <see cref="FixedIntervalOptions" /> record.
/// </summary>
public FixedIntervalOptions() : this(true, default, default)
public FixedIntervalOptions() : this(RetryStrategy.DefaultFirstFastRetry, default, default)
{
}

/// <summary>
/// Gets or sets the retry count.
/// </summary>
public int RetryCount
{
get => this.retryCount;
init => this.retryCount = value.ThrowIfNegative();
}
public int RetryCount { get; init; } = RetryCount.ThrowIfNegative();

/// <summary>
/// Gets the time interval between retries.
/// </summary>
public TimeSpan RetryInterval
{
get => this.retryInterval;
init => this.retryInterval = value.ThrowIfNegative();
}
public TimeSpan RetryInterval { get; init; } = RetryInterval.ThrowIfNegative();
}

/// <summary>
/// Represents the options for <see cref="Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.Incremental" /> retry strategy.
/// </summary>
public record IncrementalOptions(bool FastFirstRetry, int RetryCount, TimeSpan InitialInterval, TimeSpan Increment) : RetryStrategyOptions(FastFirstRetry)
{
private readonly int retryCount;

private readonly TimeSpan initialInterval;

private readonly TimeSpan increment;

/// <summary>
/// Initializes a new instance of the <see cref="IncrementalOptions" /> record.
/// </summary>
public IncrementalOptions() : this(true, default, default, default)
public IncrementalOptions() : this(RetryStrategy.DefaultFirstFastRetry, default, default, default)
{
}

/// <summary>
/// Gets the maximum number of retry attempts.
/// </summary>
public int RetryCount
{
get => this.retryCount;
init => this.retryCount = value.ThrowIfNegative();
}
public int RetryCount { get; init; } = RetryCount.ThrowIfNegative();

/// <summary>
/// Gets the initial interval that will apply for the first retry.
/// </summary>
public TimeSpan InitialInterval
{
get => this.initialInterval;
init => this.initialInterval = value.ThrowIfNegative();
}
public TimeSpan InitialInterval { get; init; } = InitialInterval.ThrowIfNegative();

/// <summary>
/// Gets the incremental time value that will be used to calculate the progressive delay between retries..
/// </summary>
public TimeSpan Increment
{
get => this.increment;
init => this.increment = value.ThrowIfNegative();
}
public TimeSpan Increment { get; init; } = Increment.ThrowIfNegative();
}

/// <summary>
/// Represents the options for <see cref="Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.ExponentialBackoff" /> retry strategy.
/// </summary>
public record ExponentialBackoffOptions(bool FastFirstRetry, int RetryCount, TimeSpan MinBackOff, TimeSpan MaxBackOff, TimeSpan DeltaBackOff) : RetryStrategyOptions(FastFirstRetry)
{

private readonly int retryCount;

private readonly TimeSpan minBackOff;

private readonly TimeSpan maxBackOff;

private readonly TimeSpan deltaBackOff;

/// <summary>
/// Initializes a new instance of the <see cref="ExponentialBackoffOptions" /> record.
/// </summary>
public ExponentialBackoffOptions() : this(true, default, default, default, default)
public ExponentialBackoffOptions() : this(RetryStrategy.DefaultFirstFastRetry, default, default, default, default)
{
}

/// <summary>
/// Gets the maximum number of retry attempts.
/// </summary>
public int RetryCount
{
get => this.retryCount;
init => this.retryCount = value.ThrowIfNegative();
}
public int RetryCount { get; init; } = RetryCount.ThrowIfNegative();

/// <summary>
/// Gets the minimum backoff time.
/// </summary>
public TimeSpan MinBackOff
{
get => this.minBackOff;
init => this.minBackOff = value.ThrowIfNegative();
}
public TimeSpan MinBackOff { get; init; } = MinBackOff.ThrowIfNegative();

/// <summary>
/// Gets the maximum backoff time.
/// </summary>
public TimeSpan MaxBackOff
{
get => this.maxBackOff;
init => this.maxBackOff = value.ThrowIfNegative();
}
public TimeSpan MaxBackOff { get; init; } = MaxBackOff.ThrowIfNegative();

/// <summary>
/// Gets the value that will be used to calculate a random delta in the exponential delay between retries.
/// </summary>
public TimeSpan DeltaBackOff
{
get => this.deltaBackOff;
init => this.deltaBackOff = value.ThrowIfNegative();
}
public TimeSpan DeltaBackOff { get; init; } = DeltaBackOff.ThrowIfNegative();
}
8 changes: 2 additions & 6 deletions Source/TransientFaultHandling.Core/AsyncExecution.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@
/// Provides a wrapper for a non-generic <see cref="T:System.Threading.Tasks.Task" /> and calls into the pipeline
/// to retry only the generic version of the <see cref="T:System.Threading.Tasks.Task" />.
/// </summary>
internal class AsyncExecution : AsyncExecution<bool>
internal class AsyncExecution(Func<Task> taskAction, ShouldRetry shouldRetry, Func<Exception, bool> isTransient, Action<int, Exception, TimeSpan> onRetrying, bool fastFirstRetry, CancellationToken cancellationToken)
: AsyncExecution<bool>(() => StartAsGenericTask(taskAction), shouldRetry, isTransient, onRetrying, fastFirstRetry, cancellationToken)
{
private static Task<bool>? cachedBoolTask;

public AsyncExecution(Func<Task> taskAction, ShouldRetry shouldRetry, Func<Exception, bool> isTransient, Action<int, Exception, TimeSpan> onRetrying, bool fastFirstRetry, CancellationToken cancellationToken) :
base(() => StartAsGenericTask(taskAction), shouldRetry, isTransient, onRetrying, fastFirstRetry, cancellationToken)
{
}

/// <summary>
/// Wraps the non-generic <see cref="T:System.Threading.Tasks.Task" /> into a generic <see cref="T:System.Threading.Tasks.Task" />.
/// </summary>
Expand Down
44 changes: 11 additions & 33 deletions Source/TransientFaultHandling.Core/AsyncExecution`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,17 @@
/// Handles the execution and retries of the user-initiated task.
/// </summary>
/// <typeparam name="TResult">The result type of the user-initiated task.</typeparam>
internal class AsyncExecution<TResult>
internal class AsyncExecution<TResult>(Func<Task<TResult>> taskFunc, ShouldRetry shouldRetry, Func<Exception, bool> isTransient, Action<int, Exception, TimeSpan> onRetrying, bool fastFirstRetry, CancellationToken cancellationToken)
{
private readonly Func<Task<TResult>> taskFunc;

private readonly ShouldRetry shouldRetry;

private readonly Func<Exception, bool> isTransient;

private readonly Action<int, Exception, TimeSpan> onRetrying;

private readonly bool fastFirstRetry;

private readonly CancellationToken cancellationToken;

private Task<TResult>? previousTask;

private int retryCount;

public AsyncExecution(Func<Task<TResult>> taskFunc, ShouldRetry shouldRetry, Func<Exception, bool> isTransient, Action<int, Exception, TimeSpan> onRetrying, bool fastFirstRetry, CancellationToken cancellationToken)
{
this.taskFunc = taskFunc;
this.shouldRetry = shouldRetry;
this.isTransient = isTransient;
this.onRetrying = onRetrying;
this.fastFirstRetry = fastFirstRetry;
this.cancellationToken = cancellationToken;
}

internal Task<TResult> ExecuteAsync() => this.ExecuteAsyncImpl(null);

private Task<TResult> ExecuteAsyncImpl(Task? ignore)
{
if (this.cancellationToken.IsCancellationRequested)
if (cancellationToken.IsCancellationRequested)
{
if (this.previousTask is not null)
{
Expand All @@ -51,11 +29,11 @@ private Task<TResult> ExecuteAsyncImpl(Task? ignore)
Task<TResult> task;
try
{
task = this.taskFunc();
task = taskFunc();
}
catch (Exception ex)
{
if (!this.isTransient(ex))
if (!isTransient(ex))
{
throw;
}
Expand All @@ -68,14 +46,14 @@ private Task<TResult> ExecuteAsyncImpl(Task? ignore)
if (task is null)
{
throw new ArgumentException(
string.Format(CultureInfo.InvariantCulture, Resources.TaskCannotBeNull, nameof(this.taskFunc)), nameof(this.taskFunc));
string.Format(CultureInfo.InvariantCulture, Resources.TaskCannotBeNull, nameof(taskFunc)), nameof(taskFunc));
}

return task.Status switch
{
TaskStatus.RanToCompletion => task,
TaskStatus.Created => throw new ArgumentException(
string.Format(CultureInfo.InvariantCulture, Resources.TaskMustBeScheduled, nameof(this.taskFunc)), nameof(this.taskFunc)),
string.Format(CultureInfo.InvariantCulture, Resources.TaskMustBeScheduled, nameof(taskFunc)), nameof(taskFunc)),
_ => task
.ContinueWith(this.ExecuteAsyncContinueWith, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)
.Unwrap()
Expand All @@ -84,7 +62,7 @@ private Task<TResult> ExecuteAsyncImpl(Task? ignore)

private Task<TResult> ExecuteAsyncContinueWith(Task<TResult> runningTask)
{
if (!runningTask.IsFaulted || this.cancellationToken.IsCancellationRequested)
if (!runningTask.IsFaulted || cancellationToken.IsCancellationRequested)
{
return runningTask;
}
Expand All @@ -107,7 +85,7 @@ private Task<TResult> ExecuteAsyncContinueWith(Task<TResult> runningTask)
return taskCompletionSource.Task;
}

if (!this.isTransient(innerException) || !this.shouldRetry(this.retryCount++, innerException, out TimeSpan zero))
if (!isTransient(innerException) || !shouldRetry(this.retryCount++, innerException, out TimeSpan zero))
{
return runningTask;
}
Expand All @@ -117,12 +95,12 @@ private Task<TResult> ExecuteAsyncContinueWith(Task<TResult> runningTask)
zero = TimeSpan.Zero;
}

this.onRetrying(this.retryCount, innerException, zero);
onRetrying(this.retryCount, innerException, zero);
this.previousTask = runningTask;
if (zero > TimeSpan.Zero && (this.retryCount > 1 || !this.fastFirstRetry))
if (zero > TimeSpan.Zero && (this.retryCount > 1 || !fastFirstRetry))
{
return Task
.Delay(zero, this.cancellationToken)
.Delay(zero, cancellationToken)
.ContinueWith(this.ExecuteAsyncImpl, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)
.Unwrap();
}
Expand Down
15 changes: 6 additions & 9 deletions Source/TransientFaultHandling.Core/ErrorDetectionStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@
/// <summary>
/// Detects specific transient conditions.
/// </summary>
public class ErrorDetectionStrategy : ITransientErrorDetectionStrategy
/// <remarks>
/// Initializes a new instance of the <see cref="ErrorDetectionStrategy"/> class.
/// </remarks>
/// <param name="isTransient">The predicate function to detect whether the specified exception is transient. The default behavior is to catch all exceptions and retry.</param>
public class ErrorDetectionStrategy(Func<Exception, bool>? isTransient = null) : ITransientErrorDetectionStrategy
{
private readonly Func<Exception, bool> isTransient;

/// <summary>
/// Initializes a new instance of the <see cref="ErrorDetectionStrategy"/> class.
/// </summary>
/// <param name="isTransient">The predicate function to detect whether the specified exception is transient. The default behavior is to catch all exceptions and retry.</param>
public ErrorDetectionStrategy(Func<Exception, bool>? isTransient = null) =>
this.isTransient = isTransient ?? (_ => true);
private readonly Func<Exception, bool> isTransient = isTransient ?? (_ => true);

/// <summary>
/// Gets an instance of <see cref="ITransientErrorDetectionStrategy"/> that catches all exceptions as transient.
Expand Down
15 changes: 6 additions & 9 deletions Source/TransientFaultHandling.Core/ExceptionDetection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,15 @@
/// <summary>
/// Detects specific transient conditions.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="ExceptionDetection"/> class.
/// </remarks>
/// <param name="isTransient">The predicate function to detect whether the specified exception is transient.</param>
[Obsolete("Use Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.ErrorDetectionStrategy.")]
public class ExceptionDetection : ITransientErrorDetectionStrategy
public class ExceptionDetection(Func<Exception, bool>? isTransient = null) : ITransientErrorDetectionStrategy
{
private readonly Func<Exception, bool> isTransient;
private readonly Func<Exception, bool> isTransient = isTransient ?? (_ => true);

/// <summary>
/// Initializes a new instance of the <see cref="ExceptionDetection"/> class.
/// </summary>
/// <param name="isTransient">The predicate function to detect whether the specified exception is transient.</param>
public ExceptionDetection(Func<Exception, bool>? isTransient = null) =>
this.isTransient = isTransient ?? (_ => true);

/// <summary>
/// Determines whether the specified exception is transient.
/// </summary>
Expand Down
Loading

0 comments on commit 0b96fb6

Please sign in to comment.