IErrorHandler.WCF的HandleError(异常错误)方法得到意外的TimoutException
本文关键字:方法 意外 TimoutException 错误 WCF HandleError 异常 IErrorHandler | 更新日期: 2023-09-27 18:11:17
我的目的是检测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]
有两件事让我不高兴:
不是预期的
InvalidOperationException
,我首先得到TimeoutException
,然后是我扔的那个。如果我在第一次登录后关机,我的日志中会有错误的信息。回调不会立即到达,大约需要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);
}
}
}
我认为你对IErrorHandler的工作方式有一个小小的误解。我指的是MSDN。首先是ProvideFault方法
在发送响应消息之前,首先调用所有providedfault实现。当所有的ProvideFault实现都被调用并返回时,如果fault是非空的,则根据操作契约将其发送回客户端。如果在调用了所有实现之后fault为null,则响应消息由ServiceBehaviorAttribute控制。IncludeExceptionDetailInFaults属性值。
然后是HandleError方法
因为HandleError方法可以从许多不同的地方调用,所以不能保证该方法是在哪个线程上调用的。不要依赖在操作线程上调用的HandleError方法。
你看到的TimeoutException来自ServiceHost的关闭(using-Block的结束)。你可以通过在ServiceHost上设置CloseTimeout来控制它。
host.CloseTimeout = TimeSpan.FromSeconds(2);
为什么会发生超时?这是因为,从代理到服务的连接仍然存在并且没有关闭,即使代理处于故障状态。要解决这个问题,您需要在FaultedException的捕获块中调用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]