2015-09-04 70 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] 
相關問題