Policy
Transient exception handling policies that can
be applied to delegates
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Polly
{
public class Policy
{
private readonly Action<Action, Context> _exceptionPolicy;
private readonly IEnumerable<ExceptionPredicate> _exceptionPredicates;
private readonly Func<Func<CancellationToken, Task>, Context, CancellationToken, bool, Task> _asyncExceptionPolicy;
internal Policy(Action<Action> exceptionPolicy, IEnumerable<ExceptionPredicate> exceptionPredicates)
: this(delegate(Action action, Context ctx) {
exceptionPolicy(action);
}, exceptionPredicates)
{
}
internal Policy(Action<Action, Context> exceptionPolicy, IEnumerable<ExceptionPredicate> exceptionPredicates)
{
if (exceptionPolicy == null)
throw new ArgumentNullException("exceptionPolicy");
_exceptionPolicy = exceptionPolicy;
_exceptionPredicates = (exceptionPredicates ?? Enumerable.Empty<ExceptionPredicate>());
}
[DebuggerStepThrough]
public void Execute(Action action)
{
Execute(action, Context.Empty);
}
[DebuggerStepThrough]
protected void Execute(Action action, Context context)
{
if (_exceptionPolicy == null)
throw new InvalidOperationException("Please use the synchronous Retry, RetryForever, WaitAndRetry or CircuitBreaker methods when calling the synchronous Execute method.");
_exceptionPolicy(action, context);
}
[DebuggerStepThrough]
public PolicyResult ExecuteAndCapture(Action action)
{
return ExecuteAndCapture(action, Context.Empty);
}
[DebuggerStepThrough]
protected PolicyResult ExecuteAndCapture(Action action, Context context)
{
if (_exceptionPolicy != null)
try {
_exceptionPolicy(action, context);
return PolicyResult.Successful();
} catch (Exception exception) {
return PolicyResult.Failure(exception, GetExceptionType(_exceptionPredicates, exception));
}
throw new InvalidOperationException("Please use the synchronous Retry, RetryForever, WaitAndRetry or CircuitBreaker methods when calling the synchronous ExecuteAndCapture method.");
}
[DebuggerStepThrough]
protected TResult Execute<TResult>(Func<TResult> action, Context context)
{
if (_exceptionPolicy == null)
throw new InvalidOperationException("Please use the synchronous Retry, RetryForever, WaitAndRetry or CircuitBreaker methods when calling the synchronous Execute method.");
TResult result = (TResult)default(TResult);
_exceptionPolicy(delegate {
result = (TResult)action();
}, context);
return (TResult)result;
}
[DebuggerStepThrough]
public TResult Execute<TResult>(Func<TResult> action)
{
return Execute(action, Context.Empty);
}
[DebuggerStepThrough]
public PolicyResult<TResult> ExecuteAndCapture<TResult>(Func<TResult> action)
{
return ExecuteAndCapture(action, Context.Empty);
}
[DebuggerStepThrough]
protected PolicyResult<TResult> ExecuteAndCapture<TResult>(Func<TResult> action, Context context)
{
if (_exceptionPolicy != null)
try {
TResult result = (TResult)default(TResult);
_exceptionPolicy(delegate {
result = (TResult)action();
}, context);
return PolicyResult<TResult>.Successful((TResult)result);
} catch (Exception exception) {
return PolicyResult<TResult>.Failure(exception, GetExceptionType(_exceptionPredicates, exception));
}
throw new InvalidOperationException("Please use the synchronous Retry, RetryForever, WaitAndRetry or CircuitBreaker methods when calling the synchronous ExecuteAndCapture method.");
}
public static PolicyBuilder Handle<TException>() where TException : Exception
{
return new PolicyBuilder((Exception exception) => exception is TException);
}
public static PolicyBuilder Handle<TException>(Func<TException, bool> exceptionPredicate) where TException : Exception
{
return new PolicyBuilder(delegate(Exception exception) {
if (exception is TException)
return exceptionPredicate((TException)exception);
return false;
});
}
internal static ExceptionType GetExceptionType(IEnumerable<ExceptionPredicate> exceptionPredicates, Exception exception)
{
if (!exceptionPredicates.Any((ExceptionPredicate predicate) => predicate(exception)))
return ExceptionType.Unhandled;
return ExceptionType.HandledByThisPolicy;
}
internal Policy(Func<Func<CancellationToken, Task>, CancellationToken, bool, Task> asyncExceptionPolicy, IEnumerable<ExceptionPredicate> exceptionPredicates)
: this((Func<CancellationToken, Task> action, Context context, CancellationToken cancellationToken, bool continueOnCapturedContext) => asyncExceptionPolicy(action, cancellationToken, continueOnCapturedContext), exceptionPredicates)
{
}
internal Policy(Func<Func<CancellationToken, Task>, Context, CancellationToken, bool, Task> asyncExceptionPolicy, IEnumerable<ExceptionPredicate> exceptionPredicates)
{
if (asyncExceptionPolicy == null)
throw new ArgumentNullException("asyncExceptionPolicy");
_asyncExceptionPolicy = asyncExceptionPolicy;
_exceptionPredicates = (exceptionPredicates ?? Enumerable.Empty<ExceptionPredicate>());
}
[DebuggerStepThrough]
public Task ExecuteAsync(Func<Task> action)
{
return ExecuteAsync(action, Context.Empty, false);
}
[DebuggerStepThrough]
protected Task ExecuteAsync(Func<Task> action, Context context)
{
return ExecuteAsync(action, context, false);
}
[DebuggerStepThrough]
public Task ExecuteAsync(Func<Task> action, bool continueOnCapturedContext)
{
return ExecuteAsync((CancellationToken ct) => action(), Context.Empty, CancellationToken.None, continueOnCapturedContext);
}
[DebuggerStepThrough]
protected Task ExecuteAsync(Func<Task> action, Context context, bool continueOnCapturedContext)
{
return ExecuteAsync((CancellationToken ct) => action(), context, CancellationToken.None, continueOnCapturedContext);
}
[DebuggerStepThrough]
public Task ExecuteAsync(Func<CancellationToken, Task> action, CancellationToken cancellationToken)
{
return ExecuteAsync(action, Context.Empty, cancellationToken, false);
}
[DebuggerStepThrough]
protected Task ExecuteAsync(Func<CancellationToken, Task> action, Context context, CancellationToken cancellationToken)
{
return ExecuteAsync(action, context, cancellationToken, false);
}
[DebuggerStepThrough]
public Task ExecuteAsync(Func<CancellationToken, Task> action, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
return ExecuteAsync(action, Context.Empty, cancellationToken, continueOnCapturedContext);
}
protected async Task ExecuteAsync(Func<CancellationToken, Task> action, Context context, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
if (_asyncExceptionPolicy == null)
throw new InvalidOperationException("Please use the asynchronous RetryAsync, RetryForeverAsync, WaitAndRetryAsync or CircuitBreakerAsync methods when calling the asynchronous Execute method.");
await _asyncExceptionPolicy(action, context, cancellationToken, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext);
}
[DebuggerStepThrough]
public Task<PolicyResult> ExecuteAndCaptureAsync(Func<Task> action)
{
return ExecuteAndCaptureAsync(action, Context.Empty, false);
}
[DebuggerStepThrough]
protected Task<PolicyResult> ExecuteAndCaptureAsync(Func<Task> action, Context context)
{
return ExecuteAndCaptureAsync(action, context, false);
}
[DebuggerStepThrough]
public Task<PolicyResult> ExecuteAndCaptureAsync(Func<Task> action, bool continueOnCapturedContext)
{
return ExecuteAndCaptureAsync((CancellationToken ct) => action(), Context.Empty, CancellationToken.None, continueOnCapturedContext);
}
[DebuggerStepThrough]
protected Task<PolicyResult> ExecuteAndCaptureAsync(Func<Task> action, Context context, bool continueOnCapturedContext)
{
return ExecuteAndCaptureAsync((CancellationToken ct) => action(), context, CancellationToken.None, continueOnCapturedContext);
}
[DebuggerStepThrough]
public Task<PolicyResult> ExecuteAndCaptureAsync(Func<CancellationToken, Task> action, CancellationToken cancellationToken)
{
return ExecuteAndCaptureAsync(action, Context.Empty, cancellationToken, false);
}
[DebuggerStepThrough]
protected Task<PolicyResult> ExecuteAndCaptureAsync(Func<CancellationToken, Task> action, Context context, CancellationToken cancellationToken)
{
return ExecuteAndCaptureAsync(action, context, cancellationToken, false);
}
[DebuggerStepThrough]
public Task<PolicyResult> ExecuteAndCaptureAsync(Func<CancellationToken, Task> action, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
return ExecuteAndCaptureAsync(action, Context.Empty, cancellationToken, continueOnCapturedContext);
}
protected async Task<PolicyResult> ExecuteAndCaptureAsync(Func<CancellationToken, Task> action, Context context, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
if (_asyncExceptionPolicy != null)
try {
await _asyncExceptionPolicy(action, context, cancellationToken, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext);
return PolicyResult.Successful();
} catch (Exception exception) {
return PolicyResult.Failure(exception, GetExceptionType(_exceptionPredicates, exception));
}
throw new InvalidOperationException("Please use the asynchronous RetryAsync, RetryForeverAsync, WaitAndRetryAsync or CircuitBreakerAsync methods when calling the asynchronous Execute method.");
}
[DebuggerStepThrough]
public Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> action)
{
return ExecuteAsync(action, Context.Empty, false);
}
[DebuggerStepThrough]
protected Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> action, Context context)
{
return ExecuteAsync(action, context, false);
}
[DebuggerStepThrough]
public Task<TResult> ExecuteAsync<TResult>(Func<CancellationToken, Task<TResult>> action, CancellationToken cancellationToken)
{
return ExecuteAsync(action, Context.Empty, cancellationToken, false);
}
[DebuggerStepThrough]
protected Task<TResult> ExecuteAsync<TResult>(Func<CancellationToken, Task<TResult>> action, Context context, CancellationToken cancellationToken)
{
return ExecuteAsync(action, context, cancellationToken, false);
}
[DebuggerStepThrough]
public Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> action, bool continueOnCapturedContext)
{
return ExecuteAsync((CancellationToken ct) => action(), Context.Empty, CancellationToken.None, continueOnCapturedContext);
}
[DebuggerStepThrough]
protected Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> action, Context context, bool continueOnCapturedContext)
{
return ExecuteAsync((CancellationToken ct) => action(), context, CancellationToken.None, continueOnCapturedContext);
}
[DebuggerStepThrough]
public Task<TResult> ExecuteAsync<TResult>(Func<CancellationToken, Task<TResult>> action, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
return ExecuteAsync(action, Context.Empty, cancellationToken, continueOnCapturedContext);
}
protected async Task<TResult> ExecuteAsync<TResult>(Func<CancellationToken, Task<TResult>> action, Context context, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
if (_asyncExceptionPolicy == null)
throw new InvalidOperationException("Please use the asynchronous RetryAsync, RetryForeverAsync, WaitAndRetryAsync or CircuitBreakerAsync methods when calling the asynchronous Execute method.");
TResult result = (TResult)default(TResult);
await _asyncExceptionPolicy(async delegate(CancellationToken ct) {
TResult val2 = result;
TResult val = (TResult)(result = (TResult)(await action(ct).ConfigureAwait(continueOnCapturedContext)));
}, context, cancellationToken, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext);
return (TResult)result;
}
[DebuggerStepThrough]
public Task<PolicyResult<TResult>> ExecuteAndCaptureAsync<TResult>(Func<Task<TResult>> action)
{
return ExecuteAndCaptureAsync(action, Context.Empty, false);
}
[DebuggerStepThrough]
protected Task<PolicyResult<TResult>> ExecuteAndCaptureAsync<TResult>(Func<Task<TResult>> action, Context context)
{
return ExecuteAndCaptureAsync(action, context, false);
}
[DebuggerStepThrough]
public Task<PolicyResult<TResult>> ExecuteAndCaptureAsync<TResult>(Func<Task<TResult>> action, bool continueOnCapturedContext)
{
return ExecuteAndCaptureAsync((CancellationToken ct) => action(), Context.Empty, CancellationToken.None, continueOnCapturedContext);
}
[DebuggerStepThrough]
protected Task<PolicyResult<TResult>> ExecuteAndCaptureAsync<TResult>(Func<Task<TResult>> action, Context context, bool continueOnCapturedContext)
{
return ExecuteAndCaptureAsync((CancellationToken ct) => action(), context, CancellationToken.None, continueOnCapturedContext);
}
[DebuggerStepThrough]
public Task<PolicyResult<TResult>> ExecuteAndCaptureAsync<TResult>(Func<Task<TResult>> action, CancellationToken cancellationToken)
{
return ExecuteAndCaptureAsync((CancellationToken ct) => action(), Context.Empty, cancellationToken, false);
}
[DebuggerStepThrough]
protected Task<PolicyResult<TResult>> ExecuteAndCaptureAsync<TResult>(Func<Task<TResult>> action, Context context, CancellationToken cancellationToken)
{
return ExecuteAndCaptureAsync((CancellationToken ct) => action(), context, cancellationToken, false);
}
[DebuggerStepThrough]
public Task<PolicyResult<TResult>> ExecuteAndCaptureAsync<TResult>(Func<CancellationToken, Task<TResult>> action, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
return ExecuteAndCaptureAsync(action, Context.Empty, cancellationToken, continueOnCapturedContext);
}
protected async Task<PolicyResult<TResult>> ExecuteAndCaptureAsync<TResult>(Func<CancellationToken, Task<TResult>> action, Context context, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
if (_asyncExceptionPolicy != null)
try {
TResult result = (TResult)default(TResult);
await _asyncExceptionPolicy(async delegate(CancellationToken ct) {
TResult val2 = result;
TResult val = (TResult)(result = (TResult)(await action(ct).ConfigureAwait(continueOnCapturedContext)));
}, context, cancellationToken, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext);
return PolicyResult<TResult>.Successful((TResult)result);
} catch (Exception exception) {
return PolicyResult<TResult>.Failure(exception, GetExceptionType(_exceptionPredicates, exception));
}
throw new InvalidOperationException("Please use the asynchronous RetryAsync, RetryForeverAsync, WaitAndRetryAsync or CircuitBreakerAsync methods when calling the asynchronous Execute method.");
}
}
}