簡體   English   中英

WCF的IErrorHandler.HandleError(Exception error)方法正在獲取意外的TimoutException

[英]IErrorHandler.HandleError(Exception error) method of WCF is getting unexpected TimoutException

我的目的是檢測WCF服務內部未處理的錯誤,記錄它們並關閉應用程序。

為此,我使用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:僅在第二名得到我的例外是錯誤還是正常? 我可以假定對於任何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);
        }
    }
}

我認為您對IErrorHandler的工作方式有些誤解。 我指的是MSDN。 首先是ProvideFault方法

在發送響應消息之前,將首先調用所有ProvideFault實施。 當所有ProvideFault實現均已調用並返回時,並且如果fault非空,則根據操作合同將其發送回客戶端。 如果在調用所有實現之后fault為null,則響應消息由ServiceBehaviorAttribute.IncludeExceptionDetailInFaults屬性值控制。

然后是HandleError方法

因為可以從許多不同的地方調用HandleError方法,所以不能保證調用該方法的線程。 不要依賴在操作線程上被調用的HandleError方法。

您看到的TimeoutException來自ServiceHost的關閉(using-Block的結尾)。 您可以通過在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]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM