Herhaal een functie in C # totdat er geen uitzondering meer wordt gegenereerd

Ik heb een klasse die een SOAP-interface aanroept en krijgt een array met gegevens terug. Als dit verzoek echter wordt geannuleerd, wordt er een uitzondering gegenereerd. Dit is goed. Ik wil echter dat mijn programma probeert deze oproep opnieuw te plaatsen. Als de tijd verstrijkt, zou ik graag willen dat deze oproep doorgaat totdat het lukt. Hoe kan ik dit bereiken?

Bijvoorbeeld:

try
{
   salesOrdersArray = MagServ.salesOrderList(sessID, filter);
}
catch
{
   ?? What Goes Here to FORCE the above line of code to rerun until it succeeds.
}
7
Verhoog de time-out zodat het de uitzondering niet gooit?
toegevoegd de auteur ChaosPandion, de bron
Van al deze antwoorden zegt niemand dat je een specifiekere uitzondering moet maken. Wat als uw try-blok objecten in een ongeldige staat bevat om de salesOrderList te krijgen? Opnieuw proberen zal nooit lukken.
toegevoegd de auteur Marc, de bron
Je zou de methode gewoon opnieuw vanuit het blok catch kunnen bellen, MAAR - wat gebeurt er als de service voor een langere periode niet beschikbaar is? Wilt u dat de methode 24 uur recursief wordt uitgevoerd? Ik zou aanraden om het te beperken tot een vast aantal pogingen.
toegevoegd de auteur Tim, de bron

11 antwoord

Je moet gewoon voor altijd lus:

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...
    }
}

Merk op dat je bijna zeker niet eigenlijk voor altijd moet lussen. Je zou vrijwel zeker een maximum aantal pogingen moeten hebben, en waarschijnlijk alleen specifieke uitzonderingen vangen. Het vangen van alle uitzonderingen voor altijd kan verschrikkelijk zijn ... stel je voor dat salesOrderList (onconventionele methode naam, btw) gooit ArgumentNullException omdat je een bug hebt en filter null is ... wil je echt 100% van je CPU binden?

16
toegevoegd
@Tim: Ik was bezig met het toevoegen van de onderstaande paragraaf - in principe beantwoordt de code de vraag rechtstreeks, maar de paragraaf legt uit dat het waarschijnlijk geen goed idee is :)
toegevoegd de auteur Jon Skeet, de bron
@ psyklopz: U specificeert het uitzonderingstype in het catch-blok: catch (SoapException) enz. U kunt meerdere verschillende uitzonderingstypes in meerdere blokkeringsblokken specificeren.
toegevoegd de auteur Jon Skeet, de bron
Wanneer ik dit soort dingen doe, plaats ik een slaapstand van de juiste duur zodat het niet 100% van de CPU gebruikt die wacht op een service om weer online te komen. Ik zou zelfs een beperkte exponentiële backoff kunnen opnemen (zoals TCP), zodat deze snel herstelt van kleine netwerkglitches, maar eens in de paar minuten probeert als de service voor een lange periode niet beschikbaar is.
toegevoegd de auteur Gabe, de bron
+1 voor specifieke uitzonderingen, ik was mijn opmerking aan het typen tijdens de bewerking.
toegevoegd de auteur Marc, de bron
Kon dat (potentieel) systeembronnen niet verbinden en/of de oproepende app niet reageren? Klinkt niet als de beste oplossing voor mij ...
toegevoegd de auteur Tim, de bron
Je kunt ook de huidige tijd ervoor krijgen en dan wachten tot de diff zwart-wit dan en nu is> 5 sec en dan uitbreken (of hoe lang je ook wilt)
toegevoegd de auteur Mr Universe, de bron
Ik weet het, ik vang wel specifieke uitzonderingen, SOAP-fouten, enz. Vergat de geneugten van pauze, bedankt.
toegevoegd de auteur psyklopz, de bron

Als u de time-out niet kunt wijzigen, zou het onderstaande moeten werken. salesOrdersArray moet worden geïnitialiseerd naar null .

while(salesOrdersArray == null)
{
    try
    {
       salesOrdersArray = MagServ.salesOrderList(sessID, filter);
    }
    catch
    {
      //Log failure
    }
}
4
toegevoegd
+1: Ik vind dit een beetje beter dan het antwoord van Jon. Ik denk dat het de eindvoorwaarde van de lus duidelijker maakt.
toegevoegd de auteur Brian, de bron

Het is niet echt een goed idee om uitzonderingen als controlestroom te gebruiken, maar dit zal doen waar je om vroeg.

bool Caught = true;
while (Caught)
try
{
    salesOrdersArray = MagServ.salesOrderList(sessID, filter);
    Caught = false;
}
catch
{
    Caught = true;
}
1
toegevoegd

Ik gebruik een transactionele wachtrij (MSMQ) om de serviceaanroep op te slaan. Een lus zal berichten verwijderen en de dienst in een TransactionScope oproepen, als de oproep faalt, lijkt het bericht nog steeds in de wachtrij te staan. Een algemene time-out kan worden opgegeven door een vervaltijd toe te voegen aan het bericht. Deze oplossing is goed als u echt een betrouwbare oplossing wilt, omdat ik vermoedde dat het essentieel is om die bewerking te bellen.

1
toegevoegd

U moet het try/catch-blok in een lusconstruct plaatsen. Als u niet 100% van uw processor wilt consumeren, plaatst u een Thread.Sleep in het catch-blok, dus elke keer dat er een uitzondering optreedt, wacht het enige tijd, waardoor de processor andere dingen doet.

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

U kunt ook het uitzonderingstype opgeven, zodat alleen de time-outuitzondering wordt afgehandeld en andere soorten uitzonderingen 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);
    }
}

Merk op dat TimeOutException moet worden vervangen door de echte naam van de uitzondering ... Ik weet niet of dat de echte naam is.

Pas ook de slaaptijd aan, gegeven in millisecs en de hoeveelheid herhalingen, in het geval dat ik presenteerde, 100 herhalingen van 1000 ms levert een maximale wachttijd op van 1 minuut en 40 seconden, plus de bedrijfstijd zelf.

1
toegevoegd

Proberen

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

Het gedrag dat u zoekt kan een eindeloze lus veroorzaken als dit nooit lukt ...

0
toegevoegd

Hoewel ik je NIET zou aanraden om dit voor een oneindig aantal keren te doen, zou je een aparte functie uit die ene zin kunnen maken:

void GoConnect()
{
    try
    {
        salesOrdersArray = MagServ.salesOrderList(sessID, filter);
    }
    catch
    {
        GoConnect();
    }
}
0
toegevoegd
bool repeat = true;
while (repeat)
{
    try
    {
       salesOrdersArray = MagServ.salesOrderList(sessID, filter);
       repeat = false;
    }
    catch
    {
    }
}
0
toegevoegd

Ik volg dit patroon om dit probleem op te lossen:

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

Voor eeuwig proberen lijkt geen goed idee, omdat je uiteindelijk een oneindig proces kunt krijgen. Als je denkt dat je veel pogingen nodig hebt om je doel te bereiken, stel dan hier een enorm getal in.

Ik denk persoonlijk dat het verstandig is om enkele milliseconden te wachten, of seconden na eac poging Thread.Sleep (1000); voor callig Verzenden (data); --- je zou bijvoorbeeld , gebruik de parameter attempNumber om deze wachttijd te verhogen of te verlagen als u denkt dat dit verstandig is voor uw scenario.

0
toegevoegd
waarom zou ik niet? Mijn punt is hetzelfde als Jon Skeet, onder de code die hij schreef
toegevoegd de auteur Renato Gama, de bron
bedankt @asawyer wist dat niet, dus heb ik mijn antwoord bewerkt! is dat oké nu?
toegevoegd de auteur Renato Gama, de bron
het is echt logisch ...! nooit op deze manier gedacht ... Sorry dat ik je verveel, hoe zit het nu?
toegevoegd de auteur Renato Gama, de bron
gooi ex; doe dat niet!
toegevoegd de auteur asawyer, de bron
toegevoegd de auteur asawyer, de bron
Twee dingen: 1- Je wilt de basisuitzonderingklasse bijna nooit te pakken krijgen, en als je dat doet, is het vrijwel alleen voor logboekdoeleinden en dan weer gooien ing. 2 - Werp de base Uitzondering klasse nooit. U dwingt code te consumeren in de slechte praktijk om Uitzondering te vangen. Gebruik een meer geschikte subklasse of maak er zelf een.
toegevoegd de auteur asawyer, de bron
Neem even de tijd om de best practices te lezen en u bent snel in de markt. Hier zijn een aantal goede links: blogs.msdn. com/b/ericlippert/archive/2008/09/10/& hellip; codeproject.com/KB/architecture/exceptionbestpractices.aspx
toegevoegd de auteur asawyer, de bron
Oh, je bedoelt de bewerking. Ziet er goed uit! :)
toegevoegd de auteur asawyer, de bron

Probeer zoiets als dit:

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

Bewerken: Wow! Grote geesten denken hetzelfde! :)

0
toegevoegd
while(salesOrdersArray == null){

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

Dit wordt voor altijd uitgevoerd en gebruikt uitzonderingen als een lus die traag is. Is er een manier waarop u uw functie kunt aanpassen zodat deze null retourneert, in plaats van een uitzondering te genereren? Als u verwacht dat deze oproep regelmatig zal mislukken, gebruik dan geen try/catch-blok.

0
toegevoegd