Как реализовать повторную попытку n раз в случае исключения в C #? - PullRequest
8 голосов
/ 31 января 2011

Я разрабатываю приложение WPF 4.0, в котором мы получаем данные из удаленного веб-сервиса.Веб-сервис предоставляет своим клиентам более 120 методов.В случае сбоя вызова веб-службы из моего приложения WPF мне нужно повторить его n раз, что настраивается через App.Config.Как это реализовать?Есть ли шаблоны проектирования, которые решают эту проблему?

Ответы [ 7 ]

7 голосов
/ 31 января 2011
static T TryNTimes<T>(Func<T> func, int times)
{
  while (times>0)
  {
     try
     {
        return func();
     }
     catch(Exception e)
     {
       if (--times <= 0)
          throw;
     }

  }
}
3 голосов
/ 31 января 2011

Я написал этот код не так давно, чтобы сделать что-то похожее на то, что вы хотите.Его можно изменить в соответствии с вашими потребностями.Это общий метод ожидания.Передайте функцию и, если ожидаемый результат не возвращается, подождите, затем повторите попытку и выйдите после X попыток.

/// <summary>
    /// Wait for the result of func to return the expeceted result
    /// </summary>
    /// <param name="func">Function to execute each cycle</param>
    /// <param name="result">Desired result returned by func</param>
    /// <param name="waitInterval">How long to wait (ms) per cycle </param>
    /// <param name="cycles">How many times to execute func before failing</param>
    /// <returns>True if desired result was attained. False if specified time runs out before desired result is returned by func</returns>
    protected static bool WaitForEvent(Func<bool> func, bool result, int waitInterval, int cycles)
    {
        int waitCount = 0;
        while (func() != result)
        {
            if (waitCount++ < cycles)
            {
                Thread.Sleep(waitInterval);
            }
            else
            {
                return false;
            }
        }

        return true;

    }
1 голос
/ 31 января 2011
while(retries < maxTries)
   try
   {
      //retryable code here
      break;
   }
   catch(Exception ex)
   {
      if(++retries == maxTries)
         throw;
      continue;
   }

Конечно, ничего особенного, но это сделает работу.Основным шаблоном, который был бы обычен практически для любой реализации, является некоторая циклическая конструкция, содержащая и несколько управляемая try-catch;это может быть либо рекурсивный вызов, либо какой-то итерационный цикл, такой как цикл while выше.После успешной попытки убедитесь, что вы правильно вышли из цикла, и следите за повторными попытками;Невыполнение любого из них приведет к бесконечному циклу.

0 голосов
/ 31 января 2011

Вы могли бы сделать это с GOTO (задыхается)

int count = 0;
try 
{
   TryAgain:
   // Do something with your web service
}  
catch(Exception e) 
{
    if(count < numberOfAttemptsAllowed)
    {
         count++;
         goto TryAgain;
    } 
}

Я уверен, что может быть лучше, но это может сделать то, что вам нужно.

0 голосов
/ 31 января 2011

Вы можете использовать функциональный подход к этому:

class Program
{
    static T Retry<T, TException>(Func<T> thingToTry, int timesToRetry)
        where TException : Exception
    {
        // Start at 1 instead of 0 to allow for final attempt
        for (int i = 1; i < timesToRetry; i++)
        {
            try
            {
                return thingToTry();
            }
            catch (TException)
            {
                // Maybe: Trace.WriteLine("Failed attempt...");
            }
        }

        return thingToTry(); // Final attempt, let exception bubble up
    }

    static int ServiceCall()
    {
        if (DateTime.Now.Ticks % 2 == 0)
        {
            throw new InvalidOperationException("Randomly not working");
        }

        return DateTime.Now.Second;
    }

    static void Main()
    {
        int s = Retry<int, InvalidOperationException>(ServiceCall, 10);
    }
}

Это можно использовать для перехвата определенных исключений (при необходимости добавьте больше общих параметров TException).

0 голосов
/ 31 января 2011

Вот аналогичный код, который переносит нарушение обмена IO.Это та же идея: у нас есть делегат и статический метод-обертка:

/// <summary>
/// Defines a sharing violation wrapper delegate.
/// </summary>
public delegate void WrapSharingViolationsCallback();

/// <summary>
/// Wraps sharing violations that could occur on a file IO operation.
/// </summary>
/// <param name="action">The action to execute. May not be null.</param>
/// <param name="exceptionsCallback">The exceptions callback. May be null.</param>
/// <param name="retryCount">The retry count.</param>
/// <param name="waitTime">The wait time in milliseconds.</param>
public static void WrapSharingViolations(WrapSharingViolationsCallback action, WrapSharingViolationsExceptionsCallback exceptionsCallback, int retryCount, int waitTime)
{
    if (action == null)
        throw new ArgumentNullException("action");

    for (int i = 0; i < retryCount; i++)
    {
        try
        {
            action();
            return;
        }
        catch (IOException ioe)
        {
            if ((IsSharingViolation(ioe)) && (i < (retryCount - 1)))
            {
                bool wait = true;
                if (exceptionsCallback != null)
                {
                    wait = exceptionsCallback(ioe, i, retryCount, waitTime);
                }
                if (wait)
                {
                    Thread.Sleep(waitTime);
                }
            }
            else
            {
                throw;
            }
        }
    }
}

И затем мы называем это так (лямбда-выражение здесь идеально подходит):

    WrapSharingViolations(() => DoWhatever(...));
0 голосов
/ 31 января 2011
while(true)
{

try
{
 Method();
 break;
}
catch(Exception ex)
{
 i++;
 if(i == n) throw ex;
}

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...