This is a generic helper to help try some action until the given condition is met. It now works seamlessly with C# async/await keywords for asynchronous operations, which is very common in a scenario that requires retry logic.
Write retry logic for operations like web request or file operation in a more readable way rather than a try-catch nested in a loop. You can specify end conditions based on return value or exception, config the retry interval, maximum retry count and maximum retry time limitation.
usingRetry;RetryHelper.Instance.Try(()=>TryDoSomething()).UntilNoException();awaitRetryHelper.Instance.Try(async()=>awaitTryGetValueAsync()).Until(async result =>result<awaitGetQuota());// Basic usage - keep trying every 500ms foreverRetryHelper.Instance.Try(()=>TryGetValue()).Until(result =>result<0.1);// Get the result from the retried methodvarresultSmallEnough=RetryHelper.Instance.Try(()=>TryGetValue()).Until(result =>result<0.1);// Specify interval as 100 msRetryHelper.Instance.Try(()=>TryGetValue()).WithTryInterval(100).Until(result =>result<0.1);// Try 20 times maximum and throw TimeoutException if exceededRetryHelper.Instance.Try(()=>TryGetValue()).WithMaxTryCount(20).Until(result =>result<0.1);// Can also limit the total try time durationRetryHelper.Instance.Try(()=>TryGetValue()).WithTimeLimit(TimeSpan.FromSeconds(10)).Until(result =>result<0.1);// Specify the extra success/fail/timeout actionRetryHelper.Instance.Try(()=>TryGetValue()).WithMaxTryCount(20).OnSuccess(result =>Trace.TraceInformation($"Got result {result}.")).OnFailure(result =>Trace.TraceWarning($"Try failed. Got {result}.")).OnTimeout(lastResult =>Trace.TraceError("Did not get result under 0.1 in 20 times.")).Until(result =>result<0.1);OnSuccess: Executed after the condition is met.OnFailure: Executed after each failed attempt and before the next attempt.OnTimeout: Executed after all allowed attempts have failed.
Multiple callbacks of the same type can be registered. In this case, the order of invocation is not guaranteed.
RetryHelper.Instance.Try(()=>TryGetValue()).OnFailure(result =>Trace.TraceWarning($"Try failed. Got {result}.")).OnFailure(()=>Trace.TraceWarning($"As I said or will say, it failed.")).Until(result =>result<0.1);// Retry on any (non-fatal) exceptionRetryHelper.Instance.Try(()=>TryDoSomething()).UntilNoException();// Retry on specific exceptionRetryHelper.Instance.Try(()=>TryDoSomething()).UntilNoException<ApplicationException>();// Or pass the Type object as parameterRetryHelper.Instance.Try(()=>TryDoSomething()).UntilNoException(typeof(ApplicationException));// Basic usageawaitRetryHelper.Instance.Try(async()=>awaitTryGetValueAsync()).Until(result =>result<0.1);// async/await keywords can be omitted for simplicity in this caseawaitRetryHelper.Instance.Try(()=>TryGetValueAsync()).Until(result =>result<0.1);// Asynchronous until condition is also supportedawaitRetryHelper.Instance.Try(async()=>awaitTryGetValueAsync()).Until(async result =>result+awaitTryGetValueAsync()<0.2);// In case the operation is not asynchronous, but you want to use an asynchronous until condition, use TryAsyncawaitRetryHelper.Instance.TryAsync(()=>TryGetValue()).Until(async result =>result+awaitTryGetValueAsync()<0.2);// Asynchronous OnSuccess/OnFailure/OnTimeout// Note that asynchronous operation taken in OnFailure counts against TimeLimit,// i.e. when retrying with time limit, the more time taken in OnFailure, the less// retries can be performed.awaitRetryHelper.Instance.Try(async()=>awaitTryGetValueAsync()).WithTryInterval(100).WithMaxTryCount(20).OnSuccess(async result =>awaitLogToServerAsync($"Got result {result}.")).OnFailure(async result =>awaitLogToServerAsync($"Try failed. Got {result}.")).OnTimeout(async lastResult =>awaitLogToServerAsync("Did not get result under 0.1 in 20 times.")).Until(result =>result<0.1);Just like the synchronous version above, multiple asynchronous callbacks of the same type can be registered. In this case, callbacks will be invoked and awaited one by one, although the order of invocation is not guaranteed. Asynchronous callbacks will not be invoked concurrently.
RetryHelper.Instance.DefaultMaxTryCount=3;RetryHelper.Instance.DefaultMaxTryTime=TimeSpan.FromSeconds(10);RetryHelper.Instance.DefaultTryInterval=TimeSpan.FromMilliseconds(100);varretryHelper=newRetryHelper(newTraceSource("MyTraceSource")){DefaultMaxTryCount=10,DefaultMaxTryTime=TimeSpan.FromSeconds(30),DefaultTryInterval=TimeSpan.FromMilliseconds(500),};- Support passing exception type to
UntilNoException - Allow
OnFailure,OnSuccessandOnTimeoutcallbacks to take no parameter - Fixed a bug that if multiple callbacks of the same type are registered with async retry tasks, only the last one is awaited
- Replaced
Thread.SleepwithTask.DelayforAsyncRetryTask - Made
Extensions.MakeFuncobsolete which should not have been public
- Support asynchronous operations, conditions and callbacks (
async/awaitkeywords) - Updated target framework to .NET 4.5
- Fixed a bug that
OnFailureis not respected if not registered last