2015-09-04 65 views
2

我的意图是检测WCF服务内部未处理的错误,记录它们并关闭应用程序。WCF的IErrorHandler.HandleError(异常错误)方法出现意外TimoutException

为此,我使用WCF的IErrorHandler。在方法HandleError(Exception error)我收到通知,发生异常。一切正常。在问题结束时,您会看到完整的列表。下面是输出:

00000: Starting service ... 
00041: Client call ThrowUnexpected 
00056: Service is throwing [InvalidOperationException] 
00063: Client chatched [FaultException] 
10070: ErrorHandler got [TimeoutException] 
10070: ErrorHandler got [InvalidOperationException] 

有两件事情我很不满意:

  1. 相反的预期InvalidOperationException我第一次得到TimeoutException再一个我抛出。如果我在第一个日志之后记录和关闭,我的日志中将会出现错误的信息。

  2. 回调没有立即到达,只有大约10秒后。这些似乎正是那些可能是net.tcp默认值的超时秒数。对我来说已经太迟了,因为我不会在意外发生后立即终止这个过程。

问题1:是一个bug还是正常的,我让我的例外,只在第二位?我能否假设对于任何WCF配置,我都会得到这一对异常?有没有什么办法只得到抛出该方法的异常?

问题2:是否有任何方法可以立即调用,而不是在超时后调用?

上市:

internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      var stopwatch = new Stopwatch(); 
      stopwatch.Start(); 

      Console.WriteLine("{0:00000}: Starting service ...", stopwatch.ElapsedMilliseconds); 

      var instance = new SomeService(stopwatch); 
      var uri = new UriBuilder(Uri.UriSchemeNetTcp, IPAddress.Loopback.ToString(), 8085, "SomeService").Uri; 
      using (var host = new ServiceHost(instance)) 
      { 
       host.AddServiceEndpoint(typeof (ISomeService), new NetTcpBinding(), uri); 
       host.Description.Behaviors.Add(new ErrorHandlerBehavior(new ErrorHandler(stopwatch))); 
       host.Open(); 

       // DO NOT DISPOSE Channel is broken 
       var proxy = new SomeServiceProxy(uri); 
       { 
        try 
        { 
         Console.WriteLine("{0:00000}: Client call ThrowUnexpected", stopwatch.ElapsedMilliseconds); 
         proxy.ThrowUnexpected(); 
        } 
        catch (FaultException ex) 
        { 
         Console.WriteLine("{0:00000}: Client chatched [{1}]", stopwatch.ElapsedMilliseconds, 
          ex.GetType().Name); 
        } 
       } 
      } 
     } 
    } 
} 

[ServiceContract] 
public interface ISomeService 
{ 
    [OperationContract] 
    void ThrowUnexpected(); 
} 


[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 
public class SomeService : ISomeService 
{ 
    private readonly Stopwatch _stopwatch; 

    public SomeService(Stopwatch stopwatch) 
    { 
     _stopwatch = stopwatch; 
    } 

    public void ThrowUnexpected() 
    { 
     var exception = new InvalidOperationException(); 
     Console.WriteLine("{0:00000}: Service is throwing [{1}]", _stopwatch.ElapsedMilliseconds, 
      exception.GetType().Name); 
     throw exception; 
    } 
} 


public class ErrorHandler : IErrorHandler 
{ 
    private readonly Stopwatch _stopwatch; 

    public ErrorHandler(Stopwatch stopwatch) 
    { 
     _stopwatch = stopwatch; 
    } 

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault) 
    { 
    } 

    public bool HandleError(Exception error) 
    { 
     Console.WriteLine("{0:00000}: ErrorHandler got [{1}]", _stopwatch.ElapsedMilliseconds, error.GetType().Name); 
     return false; 
    } 
} 

public class SomeServiceProxy : ClientBase<ISomeService>, ISomeService 
{ 
    public SomeServiceProxy(Uri uri) 
     : base(new NetTcpBinding(), new EndpointAddress(uri)) 
    { 
    } 

    public void ThrowUnexpected() 
    { 
     Channel.ThrowUnexpected(); 
    } 
} 


public class ErrorHandlerBehavior : IServiceBehavior 
{ 
    private readonly IErrorHandler m_Handler; 

    public ErrorHandlerBehavior(IErrorHandler errorHandler) 
    { 
     m_Handler = errorHandler; 
    } 

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
    { 
    } 

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, 
     Collection<ServiceEndpoint> endpoints, 
     BindingParameterCollection bindingParameters) 
    { 
    } 

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) 
    { 
     foreach (var channelDispatcherBase in serviceHostBase.ChannelDispatchers) 
     { 
      var dispatcher = (ChannelDispatcher) channelDispatcherBase; 
      dispatcher.ErrorHandlers.Add(m_Handler); 
     } 
    } 
} 

回答

2

我认为你有一个小误会怎么IErrorHandler作品。我指的是MSDN。首先是ProvideFault方法

在发送响应消息之前,首先调用所有ProvideFault实现。当所有的ProvideFault实现都被调用并返回时,如果错误不为空,它将根据操作合同发送给客户端。如果在调用所有实现后fault为null,则响应消息由ServiceBehaviorAttribute.IncludeExceptionDetailInFaults属性值控制。

然后是的HandleError方法

因为的HandleError方法可以从许多不同的地方被称为没有关于哪个线程调用该方法上进行保障。不要依赖在操作线程上调用的HandleError方法。

您所看到的TimeoutException来自ServiceHost的关闭(使用块的结束)。您可以通过在ServiceHost上设置CloseTimeout来控制这一点。

host.CloseTimeout = TimeSpan.FromSeconds(2); 

为什么超时会发生? 这是因为,即使代理处于故障状态,从代理到服务的连接仍然存在并且未关闭。为了解决这个问题,你需要在FaultedException的catch块中调用Abort。

catch (FaultException ex) 
{ 
    proxy.Abort(); 
    Console.WriteLine("{0:00000}: Client chatched [{1}]", stopwatch.ElapsedMilliseconds, 
          ex.GetType().Name); 
} 

这将导致以下的输出

00000: Starting service ... 
00005: Client call ThrowUnexpected 
00010: Service is throwing [InvalidOperationException] 
00014: Client chatched [FaultException] 
00026: ErrorHandler got [CommunicationException] 
00029: ErrorHandler got [InvalidOperationException] 
相关问题