Redo the same method call a second time

P

Peter Laan

Is there a simple way to encapsulate the functionality to redo a method call
a second time in case a specific exception is thrown? We are sending
commands to an external system and if the sessionId times out we want to
relogin to get a new sessionId and then call the same method a second time.
The method calls are actually calls to our own web service who in turn sends
the command to the external system.

My first solution using .net 1.1 uses a delegate with object[] as parameter.
RvApi is the webservice referens.

class RV
{
int _sessionId;

public void MuteAll(int confId)
{
RvCall(new object[] {confId}, new RvDel(MuteAllDel));
}
private void MuteAllDel(object[] a)
{
RvApi.MuteAll(_sessionId, (int) a[0]);
}

private delegate void RvDel(object[] a);
private void RvCall(object[] a, RvDel del)
{
for (int i = 0; i < 2; i++)
{
try
{
del(a);
return;
}
catch (Exception)
{
_sessionId = RvApi.Login();
}
}
}
}

I can't say I like this solution. The main drawback being the lack of type
safety.

Using lambda functions in 3.5 I came up with this variant.

class RV
{
int _sessionId;

public void DialOut(int confId, string phone)
{
RvCall2<int, string>(confId, phone, (x, y) =>
RvApi.DialOut(_sessionId, x, y));
}
public void MuteAll(int confId)
{
RvCall1<int>(confId, x => RvApi.MuteAll(_sessionId, x));
}

private delegate void RvDel1<A>(A a);
private delegate void RvDel2<A, B>(A a, B b);

private void RvCall1<A>(A a, RvDel1<A> del)
{
for (int i = 0; i < 2; i++)
{
try
{
del(a);
return;
}
catch (Exception)
{
// Relog to get new sessionId
}
}
}
private void RvCall2<A, B>(A a, B b, RvDel2<A, B> del)
{
// same crap to handle relogins
del(a, b);
}
}

Now I get type safety, but have to create a new delegate and RvCall-function
each time a method needs more parameters.

I suppose I could rewrite all web service methods to only use single
parameter, but then I would have to create a class for each method. Another
idea would be to use reflection. I don't think there will be any performance
problems, so it might be ok. Are there any better solutions? If not, which
one would you prefer?


Peter
 
A

Andrew Faust

Sure. Just create a wrapper function that just calls the failing function
in a loop. For example:

void Foo()
{
<Do Stuff>
while (!SomeOtherFuction())
{
}
<Do More Stuff>
}

bool SomeOtherFunction()
{
try
{
FunctionThatMayFail()
return true;
} catch (TimeoutException e)
{
return false;
}
}

void FunctionThatMayFail()
{
<Web Service Opps that may fail>
}

Note: That this code can go in to an infinite loop if the web service call
always times out.
 
P

Peter Laan

Andrew Faust said:
Sure. Just create a wrapper function that just calls the failing function
in a loop. For example:

The problem is that there are lots of those functions. So I was trying to
come up with a general solution without having to duplicate the redo logic
every time.

Peter
 
A

Andrew Faust

Fair enough. Then we need to pull it out one extra step.

void Foo()
{
<Do Stuff>

RetryableFunction(new MyRetryableFunctionDelgate(FunctionThatMayFail));
RetryableFunction(new
MyRetryableFunctionDelegate(OtherRandomFailableFunction));

<Do More Stuff>
}

delegate void MyRetryableFunctionDelegate();

void RetryableFunction(MyRetryableFunctionDelegate FunctionToRun)
{
while (!FunctionExecuter(FunctionToRun))
{
}
}

bool FunctionExecuter(MyRetryableFunctionDelegate FunctionToRun)
{
try
{
FunctionToRun()
return true;
} catch (TimeoutException e)
{
return false;
}
}

void FunctionThatMayFail()
{
<Web Service Opps that may fail>
}

void OtherRandomFailableFunction()
{
<Some Code>
}
 
P

Peter Laan

Andrew Faust said:
Fair enough. Then we need to pull it out one extra step.

But the methods have different signatures. And I just realised that some
even have return values. I wish we had macros like in C++.


Peter
 
G

Guest

That being the case I don't have a good method I can whip out off the top of
my head. I have an idea on where you could look, though. When calling methods
via reflection you have to pass in the Method Name as well as an object array
containing the parameters. You can then get back an object result value.

You might be able to create a method that takes a Method name and takes a
parameter array for all the inputs. This method would return an object. Then
that method could call the actual method via reflection. With this in place
calling your methods from the main code would look something like this.

string myValue = ExecuteRetryableMethod("MyMethod", Param1, Param2, Param3,
....) as string;

DataSet ds = ExecuteRetryableMethod("GetDataSet", Param1, Param2) as DataSet;

However, while I think this is an interesting problem and potential solution
from an academic standpoint I wouldn't recommend you actually do this. While
it may get the desired functionality, I think it will be more trouble than
it's worth in the long term. I would think maintaining and debugging this
over the next few years will consume more time and effort than you would gain
in the short term.
 
P

Peter Laan

Andrew Faust said:
That being the case I don't have a good method I can whip out off the top
of
my head. I have an idea on where you could look, though. When calling
methods
via reflection you have to pass in the Method Name as well as an object
array
containing the parameters. You can then get back an object result value.

Thank you for your replies Andrew! I think I managed to solve this in the
non-type-safe way. I'll heed your advice and stay away from reflection. So
it's either this non-type-safe solution or copying the 'relog' part to all
methods (about 20 - 30). Which one would you consider less horrible?

int _sessionId;
public void Method1()
{
MethodCaller(new object[] { }, new RvDel(Method1Del));
}
public int Method2(int param1)
{
return (int) MethodCaller(new object[] {param1}, new
RvDel(Method2Del));
}
private object Method1Del(object[] a)
{
RvApi.Method1(_sessionId);
return null;
}
private object Method2Del(object[] a)
{
return RvApi.Method2(_sessionId, (int) a[0]);
}
private delegate object RvDel(object[] a);
private object MethodCaller(object[] a, RvDel del)
{
for (int i = 0; i < 2; i++)
{
try
{
return del(a);
}
catch (Exception)
{
if (i < 1) _sessionId = RvApi.Login();
else throw;
}
}
}

Peter
 
A

Andrew Faust

What you ended up with is conceptually what I was trying to get at in my
last post but without the actual need for reflection. How many different
methods are you calling that needs this encapsulation? If it's only a few
methods I'd create a strongly typed wrapper method to do the try catch loop
for each of the methods. You would have to duplicate the loop for each of
the methods, but not for the actual method calls in your main code.

If you have a lot of different methods that need this retry capability then
the mechanism you ended up with would be the way I'd go. However, you may
want to consider putting that functionality out to a separate class. This
way you only have to be exposed to the strongly typed Method1 & Method2
methods when using this code.

--
Andrew Faust
andrew[at]andrewfaust.com
http://www.andrewfaust.com


Peter Laan said:
Andrew Faust said:
That being the case I don't have a good method I can whip out off the
top of
my head. I have an idea on where you could look, though. When calling
methods
via reflection you have to pass in the Method Name as well as an object
array
containing the parameters. You can then get back an object result value.

Thank you for your replies Andrew! I think I managed to solve this in the
non-type-safe way. I'll heed your advice and stay away from reflection.
So it's either this non-type-safe solution or copying the 'relog' part to
all methods (about 20 - 30). Which one would you consider less horrible?

int _sessionId;
public void Method1()
{
MethodCaller(new object[] { }, new RvDel(Method1Del));
}
public int Method2(int param1)
{
return (int) MethodCaller(new object[] {param1}, new
RvDel(Method2Del));
}
private object Method1Del(object[] a)
{
RvApi.Method1(_sessionId);
return null;
}
private object Method2Del(object[] a)
{
return RvApi.Method2(_sessionId, (int) a[0]);
}
private delegate object RvDel(object[] a);
private object MethodCaller(object[] a, RvDel del)
{
for (int i = 0; i < 2; i++)
{
try
{
return del(a);
}
catch (Exception)
{
if (i < 1) _sessionId = RvApi.Login();
else throw;
}
}
}

Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top