WCF的IErrorHandler.HandleError(异常错误)方法正在获得意外的TimoutException

时间:2015-09-04 16:03:32

标签: c# .net wcf

我的目的是检测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的默认值。对我来说为时已晚,因为我不会在发生意外事件后立即终止这个过程。

  3. 问题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);
            }
        }
    }
    

1 个答案:

答案 0 :(得分:3)

我认为你对IErrorHandler的工作方式有一点误解。我指的是MSDN。首先是ProvideFault方法

  

在发送响应消息之前,首先调用所有ProvideFault实现。当所有ProvideFault实现都被调用并返回时,如果fault为非null,则根据操作契约将其发送回客户端。如果在调用所有实现后fault为null,则响应消息由ServiceBehaviorAttribute.IncludeExceptionDetailInFaults属性值控制。

然后是HandleError方法

  

因为可以从许多不同的地方调用HandleError方法,所以无法保证调用该方法的线程。不要依赖于在操作线程上调用HandleError方法。

您看到的TimeoutException来自ServiceHost的关闭(using-Block的结束)。您可以通过在ServiceHost上设置CloseTimeout来控制它。

host.CloseTimeout = TimeSpan.FromSeconds(2);

为什么Timeout一直在发生? 这是因为,即使代理处于故障状态,从代理到服务的连接仍然存在且未关闭。要解决此问题,您需要在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]