C# Pass Parameter to Lambda - c#

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" });

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.

The type arguments for method MyFunction<T>(Func<T>) cannot be inferred from the usage

I am trying out this piece of code below from:
Is there a way to check if a file is in use?
But, it gives error:
The type arguments for method TimeoutFileAction(Func) cannot be inferred from the usage.
Any idea how to fix this?
TimeoutFileAction(() => { System.IO.File.etc...; return null; } );
Reusable method that times out after 2 seconds
private T TimeoutFileAction<T>(Func<T> func)
{
var started = DateTime.UtcNow;
while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
{
try
{
return func();
}
catch (System.IO.IOException exception)
{
//ignore, or log somewhere if you want to
}
}
return default(T);
}
You MUST have an output other than Type of void.
When you do this: () => { System.IO.File.etc...; return null; } the output type is void and you cannot have that for a Func<T>. If you want a Void Type then use Action.
If you want both void and T, then just write an overflow method. Se Code below:
public static void Main()
{
var today = new DateTime(2021, 10, 25, 5, 40, 0);
Console.WriteLine(today.AddHours(7).AddMinutes(36));
TimeoutFileAction(() => { Test(); });
TimeoutFileAction(Test);
}
private static string Test() => "Test";
private static void TimeoutFileAction(Action func)
{
var started = DateTime.UtcNow;
while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
{
try
{
func();
}
catch (IOException exception)
{
//ignore, or log somewhere if you want to
}
}
}
private static T TimeoutFileAction<T>(Func<T> func)
{
var started = DateTime.UtcNow;
while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
{
try
{
return func();
}
catch (IOException exception)
{
//ignore, or log somewhere if you want to
}
}
return default(T);
}

Retry logic with multiple methods

I am implementing retry logic for WCF services on the client side.
I have multiple operations in WCF service with various input parameters and return types.
I created a wrapper that can make a call to these certain methods that have no return type(void) using Action delegate. Is there any way to call methods that have various input parameters and return type.
Or is there any logic to implement retry functionality on the client side that can handle multiple WCF services.
Class RetryPolicy<T>
{
public T ExecuteAction(Func<T> funcdelegate,int? pretrycount = null,bool? pexponenialbackoff = null)
{
try
{
var T = funcdelegate();
return T;
}
catch(Exception e)
{
if (enableRetryPolicy=="ON" && TransientExceptions.IsTransient(e))
{
int? rcount = pretrycount == null ? retrycount : pretrycount;
bool? exbackoff = pexponenialbackoff == null ? exponentialbackoff : pexponenialbackoff;
int rt = 0;
for (rt = 0; rt < rcount; rt++)
{
if (exponentialbackoff)
{
delayinms = getWaitTimeExp(rt);
}
System.Threading.Thread.Sleep(delayinms);
try
{
var T = funcdelegate();
return T;
}
catch(Exception ex)
{
if (TransientExceptions.IsTransient(ex))
{
int? rcount1 = pretrycount == null ? retrycount : pretrycount;
bool? exbackoff1 = pexponenialbackoff == null ? exponentialbackoff : pexponenialbackoff;
}
else
{
throw;
}
}
}
//throw exception back to caller if exceeded number of retries
if(rt == rcount)
{
throw;
}
}
else
{
throw;
}
}
return default(T);
}
}
I use above method and make a call
public string GetCancelNumber(string property, Guid uid)
{
RetryPolicy<string> rp = new RetryPolicy<string>();
return rp.ExecuteAction(()=>Channel.GetCancelNumber(property, uid, out datasetarray));
}
I keep getting error "cannot use ref or out parameters in anonymous delegate"
Here is an example of a simple Retry method:
bool Retry(int numberOfRetries, Action method)
{
if (numberOfRetries > 0)
{
try
{
method();
return true;
}
catch (Exception e)
{
// Log the exception
LogException(e);
// wait half a second before re-attempting.
// should be configurable, it's hard coded just for the example.
Thread.Sleep(500);
// retry
return Retry(--numberOfRetries, method);
}
}
return false;
}
It will return true if the method succeed at least once, and log any exception until then.
If the method fails on every retry, it will return false.
(Succeed means completed without throwing an exception in this case)
How to use:
Assuming sample Action (void method) and sample Func (a method with a return type)
void action(int param) {/* whatever implementation you want */}
int function(string param) {/* whatever implementation you want */}
Execute a function:
int retries = 3;
int result = 0;
var stringParam = "asdf";
if (!Retry(retries, () => result = function(stringParam)))
{
Console.WriteLine("Failed in all {0} attempts", retries);
}
else
{
Console.WriteLine(result.ToString());
}
Execute an action:
int retries = 7;
int number = 42;
if (!Retry(retries, () => action(number)))
{
Console.WriteLine("Failed in all {0} attempts", retries);
}
else
{
Console.WriteLine("Success");
}
Execute a function with an out parameter (int function(string param, out int num)):
int retries = 3;
int result = 0;
int num = 0;
var stringParam = "asdf";
if (!Retry(retries, () => result = function(stringParam, out num)))
{
Console.WriteLine("Failed in all {0} attempts", retries);
}
else
{
Console.WriteLine("{0} - {1}", result, num);
}

How to retry until some condition is met

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.

Adjusting the Invoke call to cater for methods that return void and non-void types

How would I adjust the following Invoke call so that it caters for methods that return void and non-void types?
At the moment ErrorHandlingComponent.Invoke expects a Func<T> as its first parameter. I've found that when I'm trying to pass it a void method, the compiler complains.
public static T Invoke<T>(Func<T> func, int tryCount, TimeSpan tryInterval)
{
if (tryCount < 1)
{
throw new ArgumentOutOfRangeException("tryCount");
}
while (true)
{
try
{
return func();
}
catch (Exception ex)
{
if (--tryCount > 0)
{
Thread.Sleep(tryInterval);
continue;
}
LogError(ex.ToString());
throw;
}
}
}
You can't. A Func delegate is designed so that it always returns something.
The easiest way would be to create an overload of the Invoke method that takes an Action delegate rather than a Func:
public static void Invoke(Action action, int tryCount, TimeSpan tryInterval)
{
if (tryCount < 1)
{
throw new ArgumentOutOfRangeException("tryCount");
}
while (true)
{
try
{
action();
return;
}
catch (Exception ex)
{
if (--tryCount > 0)
{
Thread.Sleep(tryInterval);
continue;
}
LogError(ex.ToString());
throw;
}
}
}

Categories