How to retry until some condition is met - c#

I need to retry a certain method until it returns a non-empty Guid.
There's an awesome answer that retries based on whether there is an exception; however, I would like to generalize this class to be able to handle any specified condition.
The current usage will perform an action a specific number of times until there are no exceptions:
Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1));
or:
Retry.Do(SomeFunctionThatCanFail, TimeSpan.FromSeconds(1));
or:
int result = Retry.Do(SomeFunctionWhichReturnsInt, TimeSpan.FromSeconds(1), 4);
How can I modify this class such that it retries based on the return value of the function that I pass in?
For example, If I wanted to retry until my function returned 3:
Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1)).Until(3);
Which would mean execute SomeFunctionThatCanFail(), every 1 second, until SomeFunctionThatCanFail() = 3?
How would I generalize the usage of Retry.Do until a condition is met?
public static class Retry
{
public static void Do(
Action action,
TimeSpan retryInterval,
int retryCount = 3)
{
Do<object>(() =>
{
action();
return null;
}, retryInterval, retryCount);
}
public static T Do<T>(
Func<T> action,
TimeSpan retryInterval,
int retryCount = 3)
{
var exceptions = new List<Exception>();
for (int retry = 0; retry < retryCount; retry++) //I would like to change this logic so that it will retry not based on whether there is an exception but based on the return value of Action
{
try
{
if (retry > 0)
Thread.Sleep(retryInterval);
return action();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
}

How about creating the following interface:
public interface IRetryCondition<TResult>
{
TResult Until(Func<TResult, bool> condition);
}
public class RetryCondition<TResult> : IRetryCondition<TResult>
{
private TResult _value;
private Func<IRetryCondition<TResult>> _retry;
public RetryCondition(TResult value, Func<IRetryCondition<TResult>> retry)
{
_value = value;
_retry = retry;
}
public TResult Until(Func<TResult, bool> condition)
{
return condition(_value) ? _value : _retry().Until(condition);
}
}
And then, you'll update your Retry static class:
public static class Retry
{
// This method stays the same
// Returning an IRetryCondition does not make sense in a "void" action
public static void Do(
Action action,
TimeSpan retryInterval,
int retryCount = 3)
{
Do<object>(() =>
{
action();
return null;
}, retryInterval, retryCount);
}
// Return an IRetryCondition<T> instance
public static IRetryCondition<T> Do<T>(
Func<T> action,
TimeSpan retryInterval,
int retryCount = 3)
{
var exceptions = new List<Exception>();
for (int retry = 0; retry < retryCount; retry++)
{
try
{
if (retry > 0)
Thread.Sleep(retryInterval);
// We return a retry condition loaded with the return value of action() and telling it to execute this same method again if condition is not met.
return new RetryCondition<T>(action(), () => Do(action, retryInterval, retryCount));
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
}
You'll be able to achieve something like the following:
int result = Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1)).Until(r => r == 3);
A more functional approach
I tried to come up with a more "functional oriented" solution (somewhat similar to LINQ):
First, we would have two interfaces for executing the action:
public interface IRetryResult
{
void Execute();
}
public interface IRetryResult<out TResult>
{
TResult Execute();
}
Then, we'll need two interfaces for configuring the retry operation:
public interface IRetryConfiguration : IRetryResult
{
IRetryConfiguration Times(int times);
IRetryConfiguration Interval(TimeSpan interval);
}
public interface IRetryConfiguration<out TResult> : IRetryResult<TResult>
{
IRetryConfiguration<TResult> Times(int times);
IRetryConfiguration<TResult> Interval(TimeSpan interval);
IRetryConfiguration<TResult> Until(Function<TResult, bool> condition);
}
Finally, we'll need two implementations for both interfaces:
public class ActionRetryConfiguration : IRetryConfiguration
{
private readonly Action _action;
private readonly int? _times;
private readonly TimeSpan? _interval;
public ActionRetryConfiguration(Action action, int? times, TimeSpan? interval)
{
_action = action;
_times = times;
_interval = interval;
}
public void Execute()
{
Execute(_action, _times, _interval);
}
private void Execute(Action action, int? times, TimeSpan? interval)
{
action();
if (times.HasValue && times.Value <= 1) return;
if (times.HasValue && interval.HasValue) Thread.Sleep(interval.Value);
Execute(action, times - 1, interval);
}
public IRetryConfiguration Times(int times)
{
return new ActionRetryConfiguration(_action, times, _interval);
}
public IRetryConfiguration Interval(TimeSpan interval)
{
return new ActionRetryConfiguration(_action, _times, interval);
}
}
public class FunctionRetryConfiguration<TResult> : IRetryConfiguration<TResult>
{
private readonly Func<TResult> _function;
private readonly int? _times;
private readonly TimeSpan? _interval;
private readonly Func<TResult, bool> _condition;
public FunctionRetryConfiguration(Func<TResult> function, int? times, TimeSpan? interval, Func<TResult, bool> condition)
{
_function = function;
_times = times;
_interval = interval;
_condition = condition;
}
public TResult Execute()
{
return Execute(_function, _times, _interval, _condition);
}
private TResult Execute(Func<TResult> function, int? times, TimeSpan? interval, Func<TResult, bool> condition)
{
TResult result = function();
if (condition != null && condition(result)) return result;
if (times.HasValue && times.Value <= 1) return result;
if ((times.HasValue || condition != null) && interval.HasValue) Thread.Sleep(interval.Value);
return Execute(function, times - 1, interval, condition);
}
public IRetryConfiguration<TResult> Times(int times)
{
return new FunctionRetryConfiguration<TResult>(_function, times, _interval, _condition);
}
public IRetryConfiguration<TResult> Interval(TimeSpan interval)
{
return new FunctionRetryConfiguration<TResult>(_function, _times, interval, _condition);
}
public IRetryConfiguration<TResult> Until(Func<TResult, bool> condition)
{
return new FunctionRetryConfiguration<TResult>(_function, _times, _interval, condition);
}
}
And, lastly, the Retry static class, the entry point:
public static class Retry
{
public static IRetryConfiguration Do(Action action)
{
return new ActionRetryConfiguration(action, 1, null);
}
public static IRetryConfiguration<TResult> Do<TResult>(Func<TResult> action)
{
return new FunctionRetryConfiguration<TResult>(action, 1, null, null);
}
}
I think this approach is less buggy, and cleaner.
Also, it let you do things like these:
int result = Retry.Do(SomeIntMethod).Interval(TimeSpan.FromSeconds(1)).Until(n => n > 20).Execute();
Retry.Do(SomeVoidMethod).Times(4).Execute();

Well, if I understood everything correctly, something like this should solve your problem:
public static T Do<T>(Func<T> action, TimeSpan retryInterval, Predicate<T> predicate)
{
var exceptions = new List<Exception>();
try
{
bool succeeded;
T result;
do
{
result = action();
succeeded = predicate(result);
} while (!succeeded);
return result;
}
catch (Exception ex)
{
exceptions.Add(ex);
}
throw new AggregateException(exceptions);
}
Add this method to your retry class.
I've tried it with a sample ConsoleApplication, with this code:
class Program
{
static void Main(string[] args)
{
var _random = new Random();
Func<int> func = () =>
{
var result = _random.Next(10);
Console.WriteLine(result);
return result;
};
Retry.Do(func, TimeSpan.FromSeconds(1), i => i == 5);
Console.ReadLine();
}
}
And indeed, it stops when it randoms 5.

Microsoft's Reactive Framework (NuGet "Rx-Main") has all of the operators already built to do this kind of thing out of the box.
Try this:
IObservable<int> query =
Observable
.Defer(() =>
Observable.Start(() => GetSomeValue()))
.Where(x => x == 1)
.Timeout(TimeSpan.FromSeconds(0.1))
.Retry()
.Take(1);
query
.Subscribe(x =>
{
// Can only be a `1` if produced in less than 0.1 seconds
Console.WriteLine(x);
});

It seems like you're overthinking this:
int returnValue = -1;
while (returnValue != 3)
{
returnValue = DoStuff();
// DoStuff should include a step to avoid maxing out cpu
}
return returnValue;
Of course, "3" could be a variable that you pass into the function.

Related

Func<string, object> does not accept bool as a return type [duplicate]

This question already has an answer here:
Why won't a Func with a nullable return type fit into a Dictionary holding Funcs with object return types?
(1 answer)
Closed 9 months ago.
I am trying to create a dictionary to wrap all sql methods and perform retries regardless to the return type. However, making a Func<string, object> does not allow for bool. Is there perhaps a way to make this work?
public T RetryCommand<T>(string cmd)
{
Dictionary<Type, Func<string, object>> mapCmds = new Dictionary<Type, Func<string, object>>
{
{ typeof(DataTable), Query },
{ typeof(object), Scalar },
{ typeof(bool), Execute } /*this is the problem
error states 'bool DAL.Execute(string)' has the wrong return type*/
};
object sendBack = null;
for (int i = 0; i < 3; i++)
{
sendBack = mapCmds[typeof(T)](cmd);
//if this succeeds, break
//if there is an error, try again after a delay
}
return (T)sendBack;
}
Here is the high level definitions of the methods being called
public DataTable Query(string cmd)
{
//return DataTable from Adap.Fill()
}
public object Scalar(string cmd)
{
//return object from SQLCommand.ExecuteScalar
}
public bool Execute(string cmd)
{
//return bool from SQLCommand.ExecuteNonQuery success
}
I have changed a bit your code. But it should fulfit your expectation and it will work.
public T RetryCommand<T>(string cmd, Func<string, int, T> callingMethod, int retryCount = 0)
{
return callingMethod.Invoke(cmd, retryCount);
}
public DataTable Query(string cmd, int retryCount = 3)
{
try
{
return RetryCommand<DataTable>(cmd, Query, retryCount);
}
catch(Exception ex)
{
//Log exception
if(retryCount > 0)
{
return RetryCommand<DataTable>(cmd, Query, retryCount - 1);
}
throw;
}
}
public object Scalar(string cmd, int retryCount = 3)
{
try
{
return RetryCommand<object>(cmd, Scalar, retryCount);
}
catch (Exception ex)
{
//Log exception
if (retryCount > 0)
{
return RetryCommand<object>(cmd, Scalar, retryCount - 1);
}
throw;
}
}
public bool Execute(string cmd, int retryCount = 3)
{
try
{
return RetryCommand<bool>(cmd, Execute, retryCount);
}
catch (Exception ex)
{
//Log exception
if (retryCount > 0)
{
return RetryCommand<bool>(cmd, Execute, retryCount - 1);
}
throw;
}
}
I recommend you to use Polly so you won't need to duplicate same logic for each method but it will be handled by Polly.

C# Pass Parameter to Lambda

In the following, I need to pass nextDB to the Lambda expression in Retry:
Retry.Do(() =>
{
string nextDB = dbList.Next();
using (DataBaseProxy repo = new DataBaseProxy(nextDB))
{
return repo.DoSomething();
}
});
How do I do that? Here is my Retry class:
public static class Retry
{
public static void Do(
Action action,
int retryCount = 3)
{
Do<object>(() =>
{
action();
return null;
}, retryCount);
}
public static T Do<T>(
Func<T> action,
int retryCount = 3)
{
var exceptions = new List<Exception>();
for (int retry = 0; retry < retryCount; retry++)
{
try
{
return action();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
}
I think you want to be using Action<T> here. For example:
public static void Do<T>(
Action<T> action,
T param,
int retryCount = 3)
{
var exceptions = new List<Exception>();
for (int retry = 0; retry < retryCount; retry++)
{
try
{
action(param);
return;
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
You would call this function like this:
Do(s => {
Console.WriteLine(s);
}, "test", 3);
Based on your comments, it seems that you want to pass in multiple databases and try each one in succession until you find one that works. One simple option would be to remove to retryCount and instead pass in your array.
public static void Do<T>(
Action<T> action,
IEnumerable<T> items)
{
var exceptions = new List<Exception>();
foreach(var item in items)
{
try
{
action(item);
return;
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
And now you call it something like this:
Do(s => {
Console.WriteLine(s);
}, new[] { "db1", "db2", "db3" });

C# Best way to share logic and re-use code

The given function returns a KPI value, first it checks its cache, then it performs its logic, caches the result and returns a value, handling a failure condition.
How am I best to re-use the caching, error handling logic. What I essentially want to create is a function that just performs the necessary logic with boiler plate code abstracted away and re-used across multiple similar functions.
public static int CurrentEmployees()
{
if (HttpRuntime.Cache["CurrentEmployees"] == null)
{
try
{
int CurrentEmployees = Employee.Load().Count(x => x.DateFinished == null && !x.Contractor && x.DateStarted < DateTime.Now);
HttpRuntime.Cache.Insert("CurrentEmployees", CurrentEmployees, null, DateTime.Now.AddMinutes(20), new TimeSpan(0, 10, 0));
return CurrentEmployees;
}
catch(Exception e)
{
//TODO: Report this
return -1;
}
}
else
return (int)HttpRuntime.Cache["CurrentEmployees"];
}
As the boilerplate code is wrapped around the logic it is difficult for me to simply push these into other function calls.
Here's how you could create a generic method to cache whatever you want and reuse this logic.
public static T Cache<T>(string key, Func<T> loadFunction, Func<T> errorHandler)
{
if (HttpRuntime.Cache[key] == null)
{
try
{
T value = loadFunction();
HttpRuntime.Cache.Insert(key, value , null, DateTime.Now.AddMinutes(20), new TimeSpan(0, 10, 0));
return value;
}
catch(Exception e)
{
//TODO: Report this
return errorHandler();
}
}
else
return (T)HttpRuntime.Cache[key];
}
Usage:
public static int CurrentEmployees()
{
return Cache<int>("CurrentEmployees",
() => Employee.Load().Count(x => x.DateFinished == null && !x.Contractor && x.DateStarted < DateTime.Now),
() => -1);
}
Agree with answer from #DLeh but I would write it like this:
public static class HelperFunctions
{
public static Func<T> Cache<T>(this Func<T> inner, string cacheName)
{
return () =>
{
if (HttpRuntime.Cache[cacheName] == null)
{
var result = inner();
HttpRuntime.Cache.Insert(cacheName, inner(), null, DateTime.Now.AddMinutes(20), new TimeSpan(0, 10, 0));
return result;
}
return (T)HttpRuntime.Cache[cacheName];
};
}
public static Func<T> OnError<T>(this Func<T> inner, Func<Exception, T> onError)
{
return () =>
{
try
{
return inner();
}
catch (Exception e)
{
return onError(e);
}
};
}
}
Usage:
public static class Employees
{
public static int CurrentEmployees()
{
return (new Func<int>(() => Employee.Load().Count(x => x.DateFinished == null && !x.Contractor && x.DateStarted < DateTime.Now)))
.Cache("CurrentEmployees")
.OnError(e => -1)
();//TODO: log?
}
}
This way we separate caching logic from error handling (following single responsibility principle) and are able to reuse/compose each separately. So when you add another function like this you won't have to change Caching function.

Finding all references to a method called by using reflection in Visual Studio

I have a class that implements ISupportIncrementalLoading interface. In this interface I am using reflection in order to get data from the data source. As I am passing method names as a string when I use "Find All References" in Visual Studio, it cannot find these classes.
This may cause problems if I change the signature of my method as I will not get any compile time errors, instead I will get a runtime error.
Is there a way to pass the name of the method that will let visual studio to accept it as a reference to the method.
This is my IncrementalCollection Class.
public class IncrementalCollection<T> : ObservableCollection<T>, ISupportIncrementalLoading
{
private bool hasMoreItems;
private int currentPage;
private string datasourceClass;
private string datasourceMethod;
private List<Object> parameters;
public IncrementalCollection(string datasourceClass, string datasourceMethod, List<Object> parameters)
{
this.datasourceClass = datasourceClass;
this.datasourceMethod = datasourceMethod;
this.parameters = parameters;
this.hasMoreItems = true;
}
public void ResetCollection(List<Object> parameters)
{
this.parameters = parameters;
currentPage = 0;
this.Clear();
}
public bool HasMoreItems
{
get { return hasMoreItems; }
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
var dispatcher = Window.Current.Dispatcher;
return Task.Run<LoadMoreItemsResult>(
async () =>
{
uint resultCount = 0;
List<Object> modifiedParameters = new List<object>(this.parameters);
modifiedParameters.Add(++this.currentPage);
Type type = Type.GetType(this.datasourceClass);
MethodInfo method = type.GetTypeInfo().GetDeclaredMethod(this.datasourceMethod);
IList<T> result = await (Task<IList<T>>)method.Invoke(Activator.CreateInstance(type, null), modifiedParameters.ToArray());
if (result == null || result.Count == 0)
{
hasMoreItems = false;
}
else
{
resultCount = (uint)result.Count;
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() =>
{
foreach (T item in result)
this.Add(item);
});
}
return new LoadMoreItemsResult() { Count = resultCount };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}
This is how I initialise IncrementalCollection, here I want to pass the name of the method by referencing it somehow.
List<Object> parameters = new List<Object>();
parameters.Add(url);
parameters.Add(null);
parameters.Add(null);
IncrementalCollection<User> _users = new IncrementalCollection<User>(_dataService.GetType().FullName, "GetUserList", parameters);
Thanks for your helps in advance.
You can just use the basic Func-style delegates. In your case, the consumer is passing in a delegate that takes one additional parameter:
var _users = new IncrementalCollection<User>(page => new DataService().GetUserList(uri, null, null, page));
And your IncrementalCollection is modified to use Func:
public class IncrementalCollection<T> : ObservableCollection<T>, ISupportIncrementalLoading
{
private bool hasMoreItems;
private int currentPage;
private Func<int, Task<IList<T>>> func;
public IncrementalCollection(Func<int, Task<IList<T>>> func)
{
this.func = func;
this.hasMoreItems = true;
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
var dispatcher = Window.Current.Dispatcher;
return Task.Run<LoadMoreItemsResult>(async () =>
{
uint resultCount = 0;
var result = await func(++this.currentPage);
if (result == null || result.Count == 0)
{
hasMoreItems = false;
}
else
{
resultCount = (uint)result.Count;
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() =>
{
foreach (T item in result)
this.Add(item);
});
}
return new LoadMoreItemsResult() { Count = resultCount };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}
However, I can't just let that dispatcher code go, and I doubt the Task.Run is necessary, since this appears to be I/O-based. It's better to write it more naturally with async/await:
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
return DoLoadMoreItemsAsync(count).AsAsyncOperation();
}
private async Task<LoadMoreItemsResult> DoLoadMoreItemsAsync(uint count)
{
var result = await func(++this.currentPage);
if (result == null || result.Count == 0)
{
hasMoreItems = false;
}
else
{
foreach (T item in result)
this.Add(item);
}
return new LoadMoreItemsResult() { Count = result == null ? 0 : result.Count };
}

Is there a way to avoid overloading a method with Func<T, ...> argument for each Func parameter count

I know the question sounds a bit wierd. Sorry for that, having difficulties trying to get where i want to and even explain it. To make it simple, i have a method with Func<T> argument. But i do not always pass a parameterless Action to that method, i need varying numbers of parameters and i'm trying to find a way to avoid overloading my method everytime number of parameters needed is increased
Here is my generic class, I need to overload GetInstance method:
public class MethodResult<T>
{
public T Result { get; set; }
public bool IsResulted { get; set; }
public Exception Error { get; set; }
private MethodResult() { }
public static MethodResult<T> GetInstance<T>(Func<T> method)
{
MethodResult<T> obj = new MethodResult<T>();
try
{
obj.Result = method();
obj.IsResulted = true;
obj.Error = null;
}
catch (Exception ex)
{
obj.Result = default(T);
obj.IsResulted = false;
obj.Error = ex;
}
return obj;
}
public static MethodResult<T> GetInstance<T, T1>(Func<T1, T> method, T1 param1)
{
MethodResult<T> obj = new MethodResult<T>();
try
{
obj.Result = method(param1);
obj.IsResulted = true;
obj.Error = null;
}
catch (Exception ex)
{
obj.Result = default(T);
obj.IsResulted = false;
obj.Error = ex;
}
return obj;
}
}
And here is sample showing how i want to make use of it:
public static void Main(string[] args)
{
var x = MethodResult<int>.GetInstance(IntResult, 5);
Console.WriteLine("Result: {0}, IsResulted: {1}, ErrorMessage: {2}", x.Result, x.IsResulted, (x.Error == null ? "null" : x.Error.Message));
var y = MethodResult<string>.GetInstance(SayHello);
Console.WriteLine("Result: {0}, IsResulted: {1}, ErrorMessage: {2}", y.Result, y.IsResulted, (y.Error == null ? "null" : y.Error.Message));
Console.Read();
}
public static int IntResult(int x) { return x + 1; }
public static int IntResult(int x, int y) { return x + y; }
public static string SayHello() { return "Hello world!"; }
In order to be able to use IntResult(int x, int y) i have to overload GetInstance method with signiture:
public static MethodResult<T> GetInstance<T, T1, T2>(Func<T1, T2, T> method, T1 param1, T2 param2)
It's obvious that this will become very time consuming as it's already been annoying. Is there a way to avoid that much overloading ?
Instead of passing the function to call and the parameters along with it, you can pass a parameterless anonymous delegate calling the method you want to point to with parameters you want. Just remove any overloads of GetInstance just keep:
public static MethodResult<T> GetInstance(Func<T> method)
{
MethodResult<T> obj = new MethodResult<T>();
try
{
obj.Result = method();
obj.IsResulted = true;
obj.Error = null;
}
catch (Exception ex)
{
obj.Result = default(T);
obj.IsResulted = false;
obj.Error = ex;
}
return obj;
}
And then call it as following:
int n = 1;
var x = MethodResult<string>.GetInstance(() => SayHello());
var y = MethodResult<string>.GetInstance(() => IntResult(2));
var z = MethodResult<int>.GetInstance(() => IntResult(n, 9));

Categories