我的目的是检测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:这是一个错误还是我只在第二个地方获得例外是否正常?我可以假设对于任何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);
}
}
}
答案 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]