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

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

Вопрос:

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

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

  • Задержка X секунд перед повторной попыткой
  • Закройте и воссоздайте клиент New () 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's 
         //  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/questions/1427926/detecting-client-death-in-wcf-duplex-contracts/1428238#1428238
        }
        finally
        {
            if (!success)
            {
                proxy.Abort();
            }
        }
    }

person halfbit    schedule 25.05.2011    source источник
comment
Ради Пита, пожалуйста, избавьтесь от e в throw e в этих блоках catch. Он отбрасывает всю трассировку стека перед ним и превращает логическое устранение неполадок в игру в угадывание.   -  person StingyJack    schedule 11.03.2020


Ответы (4)


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

После того, как я возился с этим в течение нескольких лет, я предпочитаю приведенный ниже код (после просмотра этого сообщения в блоге с машины обратного пути) для обработки повторных попыток 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 );
       }

    }
}
person halfbit    schedule 28.02.2012
comment
У меня тот же вопрос, что и у вас. Это тот подход, который вы использовали? И для звонков, которые требуют времени (2-20 секунд), какой шаблон следует использовать там. - person paparazzo; 09.06.2012
comment
В общем, я использовал токен отмены .NET 4 для выдачи тайм-аутов моему коду, но еще не подключил его к WCF. В MSDN есть хороший образец токенов отмены. - person halfbit; 09.06.2012
comment
Работал отлично, исправляя строку if (mostRecentEx! = Null) на if (success == false && mostRecentEx! = Null) .. - person MüllerDK; 10.09.2013
comment
Я не мог использовать T proxy =, поскольку у T нет метода Close или Abort. Мне пришлось придерживаться IClientChannel proxy = , как код в упомянутом сообщении. - person Erikest; 15.11.2013
comment
@ makerofthings7, я думаю, что большинство операторов catch не требуются: ChannelTerminatedException, ChannelTerminatedException, ChannelTerminatedException - все происходит от CommunicationException; просто ловя, что ловишь их всех. Единственным исключением (каламбур :)) в этой структуре, похоже, является System.ServiceModel.FaultException, который НЕ является проблемой с каналом, но проблемой с целевым кодом на другом конце, который возвращает исключение обратно в Канал WCF. - person port443; 11.06.2014
comment
@ port443 Будет ли обработчик CommunicationException всегда откладывать, или это будет исключение;)? Например. Попросите пользователя убедиться, что имя сервера верное, если он не найден (или запустите VPN). В противном случае это имеет смысл. - person halfbit; 11.06.2014
comment
@ themakerofthings7, порядок имеет значение: в моем понимании мы должны сначала поймать FaultException (поскольку это связано с логикой), затем CommunicationException как причину для повторной попытки, затем TimeoutException, затем Excpetion, чтобы уловить все оставшееся. Если только я что-то не упустил. - person port443; 11.06.2014
comment
@ makerofthings7, еще одна вещь: вы повторно используете прокси в цикле после вызова для него Abort (); вы видели, как это работает на практике? Как в ответах SO, так и в документации MS говорится, что после Close-d / Abort-ed его больше нельзя использовать. - person port443; 12.06.2014
comment
@ port443 - Вы правы, я исправил это в своем коде, но неплохо обновить эту ссылку SO. Я смутно помню, что обратный порядок лучше всего подходит для моих целей, но я +1 к вашим мыслям о порядке исключения на случай, если другие захотят тот же маршрут. - person halfbit; 12.06.2014
comment
@ port433 Наконец-то я еще раз взглянул на это ... да, порядок имеет значение, как вы сказали. ServerTooBusyException ChannelTerminatedException и EndpointNotFoundException должны быть выше CommunicationException. Почему? Все предыдущие являются производными от исключений связи, поэтому CE должен быть последним, если вы хотите их перехватить. - person halfbit; 04.08.2014
comment
Почему ChannelFactory ‹T› не утилизируется? В этом больше нет необходимости или это просто не часть кода примера? - person Herman Cordes; 13.11.2014
comment
Мы видели еще одно исключение WCF, ProtocolException. Это происходит, когда служба на другой стороне, например, перестает правильно согласовывать HTTPS, но в противном случае подключается. - person Chris Moschini; 08.04.2015
comment
Какая-либо конкретная причина выброса e в последнем блоке catch? - person yue shi; 20.02.2016
comment
Используете Polly для повторных попыток? - person Kiquenet; 22.03.2018
comment
Отличный пример! Решение моей проблемы было очень вдохновляющим :-) Что, если бы у вас было много методов, возвращающих самые разные вещи. Например: PlaceOrder (возвращает пустоту), GetOrder (возвращает int) и GetAllOrders () (возвращает int []). Как бы Вы это сделали? - person progLearner; 10.02.2021

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

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

http://smartwcfclient.codeplex.com/

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

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

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

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


 reusableSW.Dispose();
person halfbit    schedule 01.06.2011

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

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

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

person Frode Stenstrøm    schedule 29.05.2011
comment
Это старый, но если я правильно помню, мы создали наши собственные классы исключений в этом проекте, который был подклассом класса исключения - person Frode Stenstrøm; 31.12.2016
comment
Прости. нет. Это было более 6 лет назад - person Frode Stenstrøm; 24.03.2018

Ссылки ниже могут помочь в обработке исключений WCF:

http://www.codeproject.com/KB/WCF/WCFErrorHandling.aspx

http://msdn.microsoft.com/en-us/library/cc949036.aspx

person Pradeep    schedule 31.05.2011
comment
+1 - Отличные ссылки. Можете ли вы помочь (возможно, отредактировав свой ответ) с текстовым описанием того, как я могу использовать IExceptionToFaultConverter или другие классы для фактической реализации корректирующего поведения WCF для тайм-аутов и т. Д.? - person halfbit; 31.05.2011