-
Notifications
You must be signed in to change notification settings - Fork 57
/
Copy pathAutoRetryAttribute.cs
140 lines (120 loc) · 3.86 KB
/
AutoRetryAttribute.cs
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
using PostSharp.Aspects;
using PostSharp.Serialization;
using System;
using System.Data;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace PostSharp.Samples.AutoRetry
{
/// <summary>
/// Aspect that, when applied to a method, causes invocations of this method to be retried if the method ends with
/// specified exceptions.
/// </summary>
[PSerializable]
[LinesOfCodeAvoided(5)]
public sealed class AutoRetryAttribute : MethodInterceptionAspect
{
/// <summary>
/// Initializes a new <see cref="AutoRetryAttribute" /> with default values.
/// </summary>
public AutoRetryAttribute()
{
// Set the default values for properties.
MaxRetries = 5;
Delay = 3;
HandledExceptions = new[] { typeof(WebException), typeof(DataException) };
}
/// <summary>
/// Gets or sets the maximum number of retries. The default value is 5.
/// </summary>
public int MaxRetries { get; set; }
/// <summary>
/// Gets or sets the delay before retrying, in seconds. The default value is 3.
/// </summary>
public float Delay { get; set; }
/// <summary>
/// Gets or sets the type of exceptions that cause the method invocation to be retried. The default value is
/// <see cref="WebException" /> and <see cref="DataException" />.
/// </summary>
public Type[] HandledExceptions { get; set; }
/// <summary>
/// Method invoked <i>instead</i> of the original method.
/// </summary>
/// <param name="args">Method invocation context.</param>
public override void OnInvoke(MethodInterceptionArgs args)
{
for (var i = 0; ; i++)
{
try
{
// Invoke the intercepted method.
args.Proceed();
// If we get here, it means the execution was successful.
return;
}
catch (Exception e)
{
// The intercepted method threw an exception. Figure out if we can retry the method.
if (CanRetry(i, e))
{
// Yes, we can retry. Write some message and wait a bit.
Console.WriteLine(
"Method failed with exception {0}. Sleeping {1} s and retrying. This was our attempt #{2}.",
e.GetType().Namespace, Delay, i + 1);
if (Delay > 0)
{
Thread.Sleep(TimeSpan.FromSeconds(Delay));
}
// Continue to the next iteration.
}
else
{
// No, we cannot retry. Retry the exception.
throw;
}
}
}
}
public override async Task OnInvokeAsync(MethodInterceptionArgs args)
{
for (var i = 0; ; i++)
{
try
{
// Invoke the intercepted method.
await args.ProceedAsync();
// If we get here, it means the execution was successful.
return;
}
catch (Exception e)
{
// The intercepted method threw an exception. Figure out if we can retry the method.
if (CanRetry(i, e))
{
// Yes, we can retry. Write some message and wait a bit.
Console.WriteLine(
"Method failed with exception {0}. Sleeping {1} s and retrying. This was our attempt #{2}.",
e.GetType().Namespace, Delay, i + 1);
if (Delay > 0)
{
await Task.Delay(TimeSpan.FromSeconds(Delay));
}
// Continue to the next iteration.
}
else
{
// No, we cannot retry. Retry the exception.
throw;
}
}
}
}
private bool CanRetry(int attempt, Exception e)
{
return attempt < MaxRetries &&
(HandledExceptions == null || HandledExceptions.Any(type => type.IsInstanceOfType(e)));
}
}
}