Created
February 27, 2020 19:41
-
-
Save pwelter34/5482b04498b33ea8647d69c35f4d7ee7 to your computer and use it in GitHub Desktop.
Retry code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Retry helpers for common retry scenario | |
/// </summary> | |
public static class Retry | |
{ | |
/// <summary> | |
/// The default retry count | |
/// </summary> | |
public const int RetryCount = 5; | |
/// <summary> | |
/// The default retry delay in milliseconds | |
/// </summary> | |
public const int RetryDelay = 500; | |
/// <summary> | |
/// Retries the <paramref name="retryDelegate"/> when the specified <paramref name="retryCondition"/> evaluates to true. | |
/// </summary> | |
/// <typeparam name="TResult">The type of the return value.</typeparam> | |
/// <param name="retryDelegate">The <see langword="delegate"/> to retry.</param> | |
/// <param name="retryCondition">The <see langword="delegate"/> to evaluate if should retry.</param> | |
/// <param name="retryCount">The number of times to retry.</param> | |
/// <param name="retryDelay">The amount of time in milliseconds to sleep before retry.</param> | |
public static TResult WhenCondition<TResult>(Func<TResult> retryDelegate, Func<TResult, bool> retryCondition = null, int retryCount = RetryCount, int retryDelay = RetryDelay) | |
{ | |
TResult result; | |
if (retryCondition == null) | |
{ | |
retryCondition = value => value == null; | |
} | |
while (retryCount-- > 0) | |
{ | |
result = retryDelegate(); | |
if (!retryCondition(result)) | |
{ | |
return result; | |
} | |
Thread.Sleep(retryDelay); | |
} | |
return default(TResult); | |
} | |
/// <summary> | |
/// Retries the specified <see langword="delegate"/> when an <see cref="System.Exception"/> occurs. | |
/// </summary> | |
/// <param name="retryDelegate">The <see langword="delegate"/> to retry.</param> | |
/// <param name="retryCount">The number of times to retry.</param> | |
/// <param name="retryDelay">The amount of time in milliseconds to sleep before retry.</param> | |
public static void WhenException(Action retryDelegate, int retryCount = 5, int retryDelay = RetryDelay) | |
{ | |
WhenException<Exception>(retryDelegate, retryCount, retryDelay); | |
} | |
/// <summary> | |
/// Retries the specified <see langword="delegate"/> when an exception of <typeparamref name="TException"/> occurs. | |
/// </summary> | |
/// <typeparam name="TException">The type of the exception.</typeparam> | |
/// <param name="retryDelegate">The <see langword="delegate"/> to retry.</param> | |
/// <param name="retryCount">The number of times to retry.</param> | |
/// <param name="retryDelay">The amount of time in milliseconds to sleep before retry.</param> | |
public static void WhenException<TException>(Action retryDelegate, int retryCount = RetryCount, int retryDelay = RetryDelay) | |
where TException : Exception | |
{ | |
if (retryDelegate == null) | |
{ | |
throw new ArgumentNullException(nameof(retryDelegate)); | |
} | |
while (retryCount-- > 0) | |
{ | |
try | |
{ | |
retryDelegate(); | |
return; | |
} | |
catch (TException) | |
{ | |
if (retryCount <= 0) | |
throw; | |
Thread.Sleep(retryDelay); | |
} | |
} | |
} | |
/// <summary> | |
/// Retries the specified <see langword="delegate"/> when an <see cref="System.Exception"/> occurs. | |
/// </summary> | |
/// <typeparam name="TResult">The type of the return value.</typeparam> | |
/// <param name="retryDelegate">The <see langword="delegate"/> to retry.</param> | |
/// <param name="retryCount">The number of times to retry.</param> | |
/// <param name="retryDelay">The amount of time in milliseconds to sleep before retry.</param> | |
/// <returns>The result of the <paramref name="retryDelegate"/>.</returns> | |
public static TResult WhenException<TResult>(Func<TResult> retryDelegate, int retryCount = RetryCount, int retryDelay = RetryDelay) | |
{ | |
return WhenException<Exception, TResult>(retryDelegate, retryCount, retryDelay); | |
} | |
/// <summary> | |
/// Retries the specified <see langword="delegate"/> when an exception of <typeparamref name="TException"/> occurs. | |
/// </summary> | |
/// <typeparam name="TException">The type of the exception.</typeparam> | |
/// <typeparam name="TResult">The type of the return value.</typeparam> | |
/// <param name="retryDelegate">The <see langword="delegate"/> to retry.</param> | |
/// <param name="retryCount">The number of times to retry.</param> | |
/// <param name="retryDelay">The amount of time in milliseconds to sleep before retry.</param> | |
/// <returns>The result of the <paramref name="retryDelegate"/>.</returns> | |
public static TResult WhenException<TException, TResult>(Func<TResult> retryDelegate, int retryCount = RetryCount, int retryDelay = RetryDelay) | |
where TException : Exception | |
{ | |
if (retryDelegate == null) | |
{ | |
throw new ArgumentNullException(nameof(retryDelegate)); | |
} | |
while (retryCount-- > 0) | |
{ | |
try | |
{ | |
return retryDelegate(); | |
} | |
catch (TException) | |
{ | |
if (retryCount <= 0) | |
throw; | |
Thread.Sleep(retryDelay); | |
} | |
} | |
return default(TResult); | |
} | |
} | |
[TestClass] | |
public class RetryTests | |
{ | |
[TestMethod] | |
public void RetryWhenConditionPass() | |
{ | |
Func<int> retryDelegate; | |
Func<int, bool> retryCondition; | |
int count = 1; | |
int result; | |
retryDelegate = new Func<int>(() => count++); | |
retryCondition = new Func<int, bool>(v => v < 5); | |
result = Retry.WhenCondition<int>(retryDelegate, retryCondition, retryDelay: 10); | |
result.Should().Be(5); | |
count.Should().Be(6); | |
} | |
[TestMethod] | |
public void RetryWhenConditionFail() | |
{ | |
Func<int> retryDelegate; | |
Func<int, bool> retryCondition; | |
int count = 1; | |
int result; | |
retryDelegate = new Func<int>(() => count++); | |
retryCondition = new Func<int, bool>(v => v < 10); | |
result = Retry.WhenCondition<int>(retryDelegate, retryCondition, retryDelay: 10); | |
result.Should().Be(0); | |
count.Should().Be(6); | |
} | |
[TestMethod] | |
public void RetryWhenExceptionPass() | |
{ | |
Action retryDelegate; | |
int count = 1; | |
bool pass = false; | |
retryDelegate = new Action(() => | |
{ | |
count++; | |
if (count < 5) | |
throw new InvalidOperationException("Test exception"); | |
pass = true; | |
}); | |
Retry.WhenException<InvalidOperationException>(retryDelegate, retryDelay: 10); | |
count.Should().Be(5); | |
pass.Should().BeTrue(); | |
} | |
[TestMethod] | |
public void RetryWhenExceptionThrow() | |
{ | |
Action assertionDelegate; | |
Action retryDelegate; | |
int count = 0; | |
retryDelegate = new Action(() => | |
{ | |
count++; | |
throw new InvalidOperationException("Test exception"); | |
}); | |
assertionDelegate = ()=> Retry.WhenException(retryDelegate, retryDelay: 10); | |
assertionDelegate.Should().Throw<InvalidOperationException>(); | |
count.Should().Be(5); | |
} | |
[TestMethod] | |
public void RetryWhenExceptionThrowType() | |
{ | |
Action assertionDelegate; | |
Action retryDelegate; | |
int count = 0; | |
retryDelegate = new Action(() => | |
{ | |
count++; | |
if (count < 2) | |
throw new InvalidOperationException("Test"); | |
throw new InvalidProgramException("Test"); | |
}); | |
assertionDelegate = () => Retry.WhenException<InvalidOperationException>(retryDelegate, retryDelay: 10); | |
assertionDelegate.Should().Throw<InvalidProgramException>(); | |
count.Should().Be(2); | |
} | |
[TestMethod] | |
public void RetryWhenExceptionResultPass() | |
{ | |
Func<int> retryDelegate; | |
int count = 1; | |
int result; | |
retryDelegate = new Func<int>(() => | |
{ | |
count++; | |
if (count < 5) | |
throw new InvalidOperationException("Test exception"); | |
return count; | |
}); | |
result = Retry.WhenException(retryDelegate, retryDelay: 10); | |
result.Should().Be(5); | |
count.Should().Be(5); | |
} | |
[TestMethod] | |
public void RetryWhenExceptionResultFail() | |
{ | |
Action assertionDelegate; | |
Func<int> retryDelegate; | |
int count = 0; | |
int result; | |
retryDelegate = new Func<int>(() => | |
{ | |
count++; | |
throw new InvalidOperationException("Test exception"); | |
}); | |
assertionDelegate = () => result = Retry.WhenException(retryDelegate, retryDelay: 10); | |
assertionDelegate.Should().Throw<InvalidOperationException>(); | |
count.Should().Be(5); | |
} | |
[TestMethod] | |
public void RetryWhenExceptionResultType() | |
{ | |
Action assertionDelegate; | |
Func<int> retryDelegate; | |
int count = 0; | |
int result; | |
retryDelegate = new Func<int>(() => | |
{ | |
count++; | |
if (count < 2) | |
throw new InvalidOperationException("Test"); | |
throw new InvalidProgramException("Test"); | |
}); | |
assertionDelegate = () => result = Retry.WhenException<InvalidOperationException, int>(retryDelegate, retryDelay: 10); | |
assertionDelegate.Should().Throw<InvalidProgramException>(); | |
count.Should().Be(2); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment