【问题标题】:How to handle WCF exceptions (consolidated list with code)如何处理 WCF 异常(代码合并列表)
【发布时间】:2011-09-02 02:04:56
【问题描述】:

我正在尝试扩展 this answer on SO 以使 WCF 客户端在瞬时网络故障时重试并处理 other situations that require a retry such as authentication expiration.

问题:

有哪些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();
            }
        }
    }

【问题讨论】:

  • 看在 Petes 的份上,请去掉那些 catch 块中 throw e 中的 e。它会丢弃之前的整个堆栈跟踪,并将逻辑故障排除变成猜谜游戏。

标签: c# wcf exception using-statement duplex


【解决方案1】:

编辑:多次关闭和重新打开客户端似乎效率低下。我是exploring solutions here,如果找到,我会更新和扩展此代码。 (或者如果 David Khaykin 发布了一个答案,我会将其标记为已接受)

经过几年的修改,下面的代码是我处理 WCF 重试和处理异常的首选策略 (after seeing this blog posting from the wayback machine)。

我调查了每一个异常,我想用那个异常做什么,并注意到一个共同的特征;每个需要从公共基类继承的“重试”的异常。我还注意到每个使客户端进入无效状态的 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-20 秒)的呼叫,应该在那里使用什么模式。
  • 一般来说,我一直在使用 .NET 4 取消令牌来向我的代码发出超时,但还没有将它连接到 WCF 中。 MSDN 有一个很好的取消令牌示例。
  • @themakerofthings7,顺序很重要:据我了解,我们必须首先捕获 FaultException(因为这与逻辑相关),然后是 CommunicationException 作为重试的原因,然后是 TimeoutException,然后是 Excpetion 以捕获所有剩余的.除非我错过了别的东西。
  • 为什么 ChannelFactory 没有被处理掉?这不再是必需的,还是只是示例代码的一部分?
  • 我们又看到了一个 WCF 异常,ProtocolException。例如,当另一端的服务停止正确协商 HTTPS,但以其他方式连接时,就会发生这种情况。
【解决方案2】:

我在 Codeplex 上启动了一个具有以下功能的项目

  • 允许高效重用客户端代理
  • 清理所有资源,包括 EventHandlers
  • 在双工通道上运行
  • 按呼叫服务运行
  • 支持配置构造函数,或通过工厂

http://smartwcfclient.codeplex.com/

这是一项正在进行的工作,并且受到了很多评论。对于改进它的任何反馈,我将不胜感激。

在实例模式下的示例用法:

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

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


 reusableSW.Dispose();

【讨论】:

    【解决方案3】:

    我们有一个 WCF 客户端,可以处理服务器上几乎所有类型的故障。渔获物清单很长,但并非必须如此。如果仔细观察,您会发现许多异常是异常类(以及一些其他类)的子定义。

    因此,如果您愿意,您可以简化很多事情。也就是说,以下是我们发现的一些典型错误:

    服务器超时
    服务器太忙
    服务器无法使用。

    【讨论】:

    • 这是旧的,但如果我没记错的话,我们在那个项目中创建了自己的异常类,它是异常类的子类
    • 对不起。不。这是 6 年前的事了
    【解决方案4】:

    【讨论】:

    • +1 - 很棒的链接。您能否通过文本描述来帮助(也许通过编辑您的答案)我如何使用IExceptionToFaultConverter 或其他类来实际实现针对超时等的纠正性 WCF 行为?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-11
    • 2011-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多