c# - WCF 的 IErrorHandler.HandleError(Exception error) 方法出现意外的 TimoutException

标签 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. 我首先得到的是 TimeoutException,而不是预期的 InvalidOperationException,然后是我抛出的异常。如果我在第一个之后登录并关闭,我的日志中将包含错误信息。

  2. 回调不会立即到达,大约 10 秒后才会到达。这些似乎正是 net.tcp 的默认超时秒数。对我来说已经太晚了,因为我不会在发生意外情况后立即终止进程。

问题 1:我只在第二名时获得异常(exception),这是一个错误还是正常现象?我可以假设对于任何 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 方法

All ProvideFault implementations are called first, prior to sending a response message. When all ProvideFault implementations have been called and return, and if fault is non-null, it is sent back to the client according to the operation contract. If fault is null after all implementations have been called, the response message is controlled by the ServiceBehaviorAttribute.IncludeExceptionDetailInFaults property value.

然后是HandleError方法

Because the HandleError method can be called from many different places there are no guarantees made about which thread the method is called on. Do not depend on HandleError method being called on the operation thread.

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

host.CloseTimeout = TimeSpan.FromSeconds(2);

为什么会发生超时? 这是因为,即使代理处于故障状态,从代理到服务的连接仍然存在并且没有关闭。要解决此问题,您需要在FaultedException 的catch block 中调用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]

关于c# - WCF 的 IErrorHandler.HandleError(Exception error) 方法出现意外的 TimoutException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32402243/

相关文章:

c# - 错误 "Elements defined in a namespace cannot be explicitly declared as private, protected, or protected internal"

c# - WCF 错误 'There was an error while trying to serialize parameter...'

.net - Windows Server 2008 R2上的WCF .NET 4.5

c# - 是否可以使用相关状态对象在消息检查器中传递 channel 数据?

c# - 在 TabControl 中设置 DataContext

c# - 从我的 c# 应用程序中修剪 SQL 数据库值

c# - 将 Identity 2.0 函数移动到存储库类

c# - 返回任何类型的 IQueryable

c# - 使用命名管道 WCF 服务时出现通信对象错误

c# - 我应该从函数返回数组还是集合?