-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathResilienceStrategy.cs
More file actions
92 lines (84 loc) · 3.66 KB
/
ResilienceStrategy.cs
File metadata and controls
92 lines (84 loc) · 3.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
namespace Polly;
#pragma warning disable CA1031 // Do not catch general exception types
/// <summary>
/// Resilience strategy is used to execute the user-provided callbacks.
/// </summary>
/// <remarks>
/// Resilience strategy supports various types of callbacks and provides a unified way to execute them.
/// This includes overloads for synchronous and asynchronous callbacks, generic and non-generic callbacks.
/// </remarks>
[DebuggerTypeProxy(typeof(DebuggerProxy))]
public abstract partial class ResilienceStrategy
{
/// <summary>
/// Executes the specified callback.
/// </summary>
/// <typeparam name="TResult">The type of result returned by the callback.</typeparam>
/// <typeparam name="TState">The type of state associated with the callback.</typeparam>
/// <param name="callback">The user-provided callback.</param>
/// <param name="context">The context associated with the callback.</param>
/// <param name="state">The state associated with the callback.</param>
/// <returns>An instance of <see cref="ValueTask"/> that represents an asynchronous callback.</returns>
/// <remarks>
/// This method is called by various methods exposed on <see cref="ResilienceStrategy"/>. These methods make sure that
/// <paramref name="context"/> is properly initialized with details about the execution mode.
/// <para>
/// The provided callback never throws an exception. Instead, the exception is captured and converted to an <see cref="Outcome{TResult}"/>.
/// </para>
/// <para>
/// Do not throw exceptions from your strategy implementation. Instead, return an exception instance as <see cref="Outcome{TResult}"/>.
/// </para>
/// </remarks>
protected internal abstract ValueTask<Outcome<TResult>> ExecuteCoreAsync<TResult, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
ResilienceContext context,
TState state);
private Outcome<TResult> ExecuteCoreSync<TResult, TState>(
Func<ResilienceContext, TState, Outcome<TResult>> callback,
ResilienceContext context,
TState state)
{
return ExecuteCoreAsync(
static (context, state) =>
{
var result = state.callback(context, state.state);
return new ValueTask<Outcome<TResult>>(result);
},
context,
(callback, state)).GetResult();
}
internal static ValueTask<Outcome<TResult>> ExecuteCallbackSafeAsync<TResult, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
ResilienceContext context,
TState state)
{
if (context.CancellationToken.IsCancellationRequested)
{
return new ValueTask<Outcome<TResult>>(Outcome.FromException<TResult>(new OperationCanceledException(context.CancellationToken)));
}
try
{
var callbackTask = callback(context, state);
if (callbackTask.IsCompleted)
{
return new ValueTask<Outcome<TResult>>(callbackTask.GetResult());
}
return AwaitTask(callbackTask, context.ContinueOnCapturedContext);
}
catch (Exception e)
{
return new ValueTask<Outcome<TResult>>(Outcome.FromException<TResult>(e));
}
static async ValueTask<Outcome<T>> AwaitTask<T>(ValueTask<Outcome<T>> task, bool continueOnCapturedContext)
{
try
{
return await task.ConfigureAwait(continueOnCapturedContext);
}
catch (Exception e)
{
return Outcome.FromException<T>(e);
}
}
}
}