Как обрабатывать исключения WCF (сводный список с кодом)

Я пытаюсь расширить этот ответ на SO, чтобы повторить попытку WCF-клиента при неудачных сетевых сбоях и обработать другие ситуации, требующие повторения, такие как истечение срока действия аутентификации.

Вопрос:

Каковы исключения WCF, которые необходимо обработать, и каков правильный способ их обработки?

Вот несколько примеров, которые я надеюсь увидеть вместо или в дополнение к proxy.abort():

  • Задержка X секунд перед повторной попыткой
  • Закройте и заново создайте новый() WCF-клиент. Утилизируйте старый.
  • Не пытайтесь повторить и переустановить эту ошибку.
  • Повторите N раз, затем нажмите

Поскольку маловероятно, что один человек знает все исключения или способы их решения, делитесь тем, что знаете. Я обобщу ответы и подходы в примере кода ниже.

    // USAGE SAMPLE
    //int newOrderId = 0; // need a value for definite assignment
    //Service<IOrderService>.Use(orderService=>
    //{
    //  newOrderId = orderService.PlaceOrder(request);
    //}




    /// <summary>
    /// A safe WCF Proxy suitable when sessionmode=false
    /// </summary>
    /// <param name="codeBlock"></param>
    public static void Use(UseServiceDelegateVoid<T> codeBlock)
    {
        IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
        bool success = false;
        try
        {
            codeBlock((T)proxy);
            proxy.Close();
            success = true;
        }
        catch (CommunicationObjectAbortedException e)
        {
                // Object should be discarded if this is reached.  
                // Debugging discovered the following exception here:
                // "Connection can not be established because it has been aborted" 
            throw e;
        }
        catch (CommunicationObjectFaultedException e)
        {
            throw e;
        }
        catch (MessageSecurityException e)
        {
            throw e;
        }
        catch (ChannelTerminatedException)
        {
            proxy.Abort(); // Possibly retry?
        }
        catch (ServerTooBusyException)
        {
            proxy.Abort(); // Possibly retry?
        }
        catch (EndpointNotFoundException)
        {
            proxy.Abort(); // Possibly retry?
        }
        catch (FaultException)
        {
            proxy.Abort();
        }
        catch (CommunicationException)
        {
            proxy.Abort();
        }
        catch (TimeoutException)
        {
         // Sample error found during debug: 

         // The message could not be transferred within the allotted timeout of 
         //  00:01:00. There was no space available in the reliable channel 
         //  transfer window. The time allotted to this operation may have been a 
         //  portion of a longer timeout.

            proxy.Abort();
        }
        catch (ObjectDisposedException )
        {
            //todo:  handle this duplex callback exception.  Occurs when client disappears.  
            // Source: https://stackoverflow.com/info/1427926/detecting-client-death-in-wcf-duplex-contracts/1428238#1428238
        }
        finally
        {
            if (!success)
            {
                proxy.Abort();
            }
        }
    }

Ответ 1

РЕДАКТИРОВАТЬ: Кажется, есть некоторые недостатки с закрытием и повторным открытием клиента несколько раз. Я исследует решения здесь и обновит и расширит этот код, если он найден. (или если Давид Хайкин отправит ответ, я отмечу его как принятый)

После того, как я несколько раз передумал с этим, приведенный ниже код является моей предпочтительной стратегией (после просмотра этой публикации в блоге с машины обратного пути) для работы с повторами WCF и обработки исключений.

Я исследовал каждое исключение, что я хотел бы сделать с этим исключением, и заметил общую черту; каждое исключение, которое требовало "повторения", унаследованного от общего базового класса. Я также заметил, что каждое исключение permFail, которое ставит клиент в недопустимое состояние, также поступает из общего базового класса.

В следующем примере выполняется ловушка для каждого исключения WCF, которое может пройти клиент, и расширяется для ваших собственных ошибок канала.

Пример использования WCF-клиента

Как только вы создаете прокси-сервер вашей клиентской стороны, это все, что вам нужно для его реализации.

Service<IOrderService>.Use(orderService=>
{
  orderService.PlaceOrder(request);
}

ServiceDelegate.cs

Добавьте этот файл в свое решение. Для этого файла не требуются изменения, если вы не хотите изменять количество попыток или какие исключения вы хотите обрабатывать.

public delegate void UseServiceDelegate<T>(T proxy);

public static class Service<T>
{
    public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>(""); 

    public static void Use(UseServiceDelegate<T> codeBlock)
    {
        IClientChannel proxy = null;
        bool success = false;


       Exception mostRecentEx = null;
       int millsecondsToSleep = 1000;

       for(int i=0; i<5; i++)  // Attempt a maximum of 5 times 
       {
           // Proxy cann't be reused
           proxy = (IClientChannel)_channelFactory.CreateChannel();

           try
           {
               codeBlock((T)proxy);
               proxy.Close();
               success = true; 
               break;
           }
           catch (FaultException customFaultEx)
           {
               mostRecentEx = customFaultEx;
               proxy.Abort();

               //  Custom resolution for this app-level exception
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }

           // The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
           catch (ChannelTerminatedException cte)
           {
              mostRecentEx = cte;
               proxy.Abort();
               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep  * (i + 1)); 
           }

           // The following is thrown when a remote endpoint could not be found or reached.  The endpoint may not be found or 
           // reachable because the remote endpoint is down, the remote endpoint is unreachable, or because the remote network is unreachable.
           catch (EndpointNotFoundException enfe)
           {
              mostRecentEx = enfe;
               proxy.Abort();
               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }

           // The following exception that is thrown when a server is too busy to accept a message.
           catch (ServerTooBusyException stbe)
           {
              mostRecentEx = stbe;
               proxy.Abort();

               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }
           catch (TimeoutException timeoutEx)
           {
               mostRecentEx = timeoutEx;
               proxy.Abort();

               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           } 
           catch (CommunicationException comException)
           {
               mostRecentEx = comException;
               proxy.Abort();

               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }


           catch(Exception e)
           {
                // rethrow any other exception not defined here
                // You may want to define a custom Exception class to pass information such as failure count, and failure type
                proxy.Abort();
                throw e;  
           }
       }
       if (success == false && mostRecentEx != null) 
       { 
           proxy.Abort();
           throw new Exception("WCF call failed after 5 retries.", mostRecentEx );
       }

    }
}

Ответ 2

Я начал проект Codeplex, который имеет следующие функции

  • Позволяет эффективно использовать клиентский прокси.
  • Очищает все ресурсы, включая EventHandlers
  • Работает с дуплексными каналами
  • Работает в службах для вызовов.
  • Поддерживает конструктор конфигурации или factory

http://smartwcfclient.codeplex.com/

Это незавершенное производство, и он очень сильно комментируется. Я буду благодарен за любые отзывы о его улучшении.

Пример использования примера в режиме экземпляра:

 var reusableSW = new LC.Utils.WCF.ServiceWrapper<IProcessDataDuplex>(channelFactory);

 reusableSW.Reuse(client =>
                      {
                          client.CheckIn(count.ToString());
                      });


 reusableSW.Dispose();

Ответ 3

У нас есть клиент WCF, который имеет почти любой тип сбоя на сервере. Список Catch очень длинный, но не обязательно. Если вы посмотрите внимательно, вы увидите, что многие исключения являются дочерними определениями класса исключений (и нескольких других классов).

Таким образом, вы можете упростить многое, если захотите. Тем не менее, вот некоторые типичные ошибки, которые мы улавливаем:

Тайм-аут сервера
Сервер слишком занят
Сервер недоступен.