10

I've got a class that calls a SOAP interface, and gets an array of data back. However, if this request times out, it throws an exception. This is good. However, I want my program to attempt to make this call again. If it times out, I'd like it to keep making this call until it succeeds. How can I accomplish this?

For example:

try
{
   salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch
{
   ?? What Goes Here to FORCE the above line of code to rerun until it succeeds.
}
psyklopz
  • 2,283
  • 4
  • 24
  • 29
  • 3
    Increase the timeout so it doesn't throw the exception? – ChaosPandion Oct 27 '11 at 18:40
  • You could simply call the method again from the catch block, *BUT* - what happens if the service is down for an extended period of time? Do you want the method to run recursively for 24 hours? I would recommend limiting it to a set number of retries. – Tim Oct 27 '11 at 18:40
  • 5
    Out of all of these answers, not one person tells you to catch a more specific exception. What if your try block contains objects in an invalid state to get the `salesOrderList`? Retrying will never succeed. – Marc Oct 27 '11 at 18:43

11 Answers11

18

You just need to loop forever:

while (true)
{
    try
    {
        salesOrdersArray = MagServ.salesOrderList(sessID, filter);
        break; // Exit the loop. Could return from the method, depending
               // on what it does...
    }
    catch
    {
        // Log, I suspect...
    }
}

Note that you should almost certainly not actually loop forever. You should almost certainly have a maximum number of attempts, and probably only catch specific exceptions. Catching all exceptions forever could be appalling... imagine if salesOrderList (unconventional method name, btw) throws ArgumentNullException because you've got a bug and filter is null... do you really want to tie up 100% of your CPU forever?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    Couldn't that (potentially) tie up system resources and/or make the calling app unresponsive? Doesn't sound like the best solution to me... – Tim Oct 27 '11 at 18:43
  • 1
    @Tim: I was adding the paragraph below - basically the code answers the question directly, but the paragraph explains that it's probably not a good idea :) – Jon Skeet Oct 27 '11 at 18:43
  • +1 for specific exceptions, I was typing my comment on the question during your edit. – Marc Oct 27 '11 at 18:43
  • I know, I do catch specific exceptions, SOAP errors, etc. Forgot the joys of break, thanks. – psyklopz Oct 27 '11 at 18:47
  • @psyklopz: You specify the exception type in the catch block: `catch (SoapException)` etc. You can specify multiple different exception types in multiple catch blocks. – Jon Skeet Oct 27 '11 at 18:50
  • When I do this sort of thing, I put in a `Sleep` of appropriate duration so that it doesn't use 100% of the CPU waiting for some service to come back online. I might even include a limited exponential backoff (like TCP) so that it recovers quickly from minor network glitches but tries once every few minutes if the service is down for an extended period. – Gabe Oct 28 '11 at 19:06
  • You can also get current time before and then wait till the diff b/w then and now is > 5 sec and then break out (or however long you want) – Sellorio Feb 14 '13 at 01:30
5

You must place the try/catch block inside a loop construct. If you wish not to consume 100% of your processor place a Thread.Sleep in the catch block, so everytime an exception occurs, it will wait some time, freeing the processor to do other things.

// iterate 100 times... not forever!
for (int i = 0; i < 100; i++)
{
    try {
        // do your work here;

        break; // break the loop if everything is fine
    } catch {
        Thread.Sleep(1000);
    }
}

You could also specify exception type, so that only the timeout exception is handled, and other kinds of exceptions pass-through.

// iterate 100 times... not forever!
for (int i = 0; i < 100; i++)
{
    try {
        // do your work here;

        break; // break the loop if everything is fine
    } catch (TimeOutException) {
        Thread.Sleep(1000);
    }
}

Note that, TimeOutException should be replaced by the real name of the exception... I don't know if that is the real name.

Also adjust the sleep time, given in millisecs and the amount of repeats, in the case I presented, 100 repeats of 1000ms yields a maximum wait of 1 minute and 40 seconds, plus the operation time itself.

Miguel Angelo
  • 23,796
  • 16
  • 59
  • 82
4

If you can't change the timeout, the below should work. salesOrdersArray should be initialized to null.

while(salesOrdersArray == null)
{
    try
    {
       salesOrdersArray = MagServ.salesOrderList(sessID, filter);
    }
    catch
    {
       // Log failure
    }
}
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
  • +1: I like this a bit better than Jon's answer. I think it makes the end condition of the loop more obvious. – Brian Oct 27 '11 at 21:16
2

It its not gernally a good idead to use exceptions as control flow, but this will do what you requested.

bool Caught = true;
while (Caught)
try
{
    salesOrdersArray = MagServ.salesOrderList(sessID, filter);
    Caught = false;
}
catch
{
    Caught = true;
}
rerun
  • 25,014
  • 6
  • 48
  • 78
1

I will use a transactional queue (MSMQ) to store the service call. A loop will dequeue messages and call the service in a TransactionScope, if the call fails the message appear to be still in the queue. An ov erall timeout can be specified by adding a time to expire in the message. This solution is good if you really want a reliable solution since I guessed that calling that operation is critical.

Felice Pollano
  • 32,832
  • 9
  • 75
  • 115
0
bool repeat = true;
while (repeat)
{
    try
    {
       salesOrdersArray = MagServ.salesOrderList(sessID, filter);
       repeat = false;
    }
    catch
    {
    }
}
Adi Lester
  • 24,731
  • 12
  • 95
  • 110
Albin Sunnanbo
  • 46,430
  • 8
  • 69
  • 108
0

Try

bool failed = false;
do {
 try
 {
  salesOrdersArray = MagServ.salesOrderList(sessID, filter);
 }
 catch
 {
  failed = true;
 }
} while(failed);

The behavior you are after might cause an endless loop if this never succeeds though...

Jonatan Hedborg
  • 4,382
  • 21
  • 30
0

Try something like this:

var failed = true;
while (failed)
{
  try 
  {
    salesOrdersArray = MagServ.salesOrderList(sessID, filter); 
    failed = false;
  }
  catch
  {
  }
}

Edit: Wow! Great minds think alike! :)

Rob H
  • 1,840
  • 16
  • 25
0

Although I would NOT recommend you to do this for an infinite number of times, you could make a separate function out of that one sentence:

void GoConnect()
{
    try
    {
        salesOrdersArray = MagServ.salesOrderList(sessID, filter);
    }
    catch
    {
        GoConnect();
    }
}
Marnix
  • 6,384
  • 4
  • 43
  • 78
0
while(salesOrdersArray == null){

  try
  {
    salesOrdersArray = MagServ.salesOrderList(sessID, filter);
  }
  catch(salesOrderException e)
  {
     log(e.message);
  }
}

This will run forever, and is using exceptions as a loop which is slow. Is there a way you can modify your function that it returns null, instead of throwing an exception? If you're expecting that this call will fail regularly, don't use a try/catch block.

petebowden
  • 900
  • 2
  • 8
  • 21
0

I follow this pattern in order to solve this problem:

    public void Send(String data, Int32 attemptNumber)
    {
        try
        {
            yourCodeHere(data);
        }
        catch (WebException ex)
        {
            if (attemptNumber > 0)
                Send(data, --attemptNumber);
            else
                throw new AttemptNumberExceededException("Attempt number exceeded!", ex);
        }
        catch (Exception ex)
        {
            //Log pourpose code goes here!
            throw;
        }
    }

Trying forever seems not to be a good idea as you may end up having an infinite process. If you think you need many attempts to achieve your goal just set huge number here.

I personally think its wise to wait some milliseconds, or seconds after eac attempt Thread.Sleep(1000); before callig Send(data); --- you could for example, use the attempNumber variable to increse or decrease this waiting time if you think its wise for your scenario.

Renato Gama
  • 16,431
  • 12
  • 58
  • 92
  • why shouldnt I? My point is the same as Jon Skeet, below the code he wrote – Renato Gama Oct 28 '11 at 02:41
  • 2
    Don't `throw ex;` http://stackoverflow.com/questions/178456/what-is-the-proper-way-to-re-throw-an-exception-in-c http://stackoverflow.com/questions/22623/net-throwing-exceptions-best-practices – asawyer Oct 28 '11 at 05:31
  • thanks @asawyer didn't know about that so I edited my answer! is that ok now? – Renato Gama Oct 28 '11 at 07:32
  • Two things: 1- You almost never want to catch the base exception class, and if you do, it's pretty much only for logging purposes, and then `throw`ing again. 2 - Never throw the base `Exception` class. You are forcing consuming code into the bad practice of catching `Exception`. Use a more appropriate subclass, or create your own. – asawyer Oct 28 '11 at 11:52
  • it makes real sense...! never thought this way... sorry for boring you, what about now? – Renato Gama Oct 28 '11 at 18:58
  • Just take a little time to read up on best practices, and you'll be in business in no time. Heres a couple good links: http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx – asawyer Oct 28 '11 at 19:01