FailFast was invoked.
Message: ICallContextInitializer.BeforeInvoke threw an exception of type System.Exception: 调用MyCallContextInitializer.AfterInvoke()出错!
Stack Trace: at System.ServiceModel.Diagnostics.ExceptionUtility.TraceFailFast(String message, EventLogger logger)
at System.ServiceModel.Diagnostics.ExceptionUtility.TraceFailFast(String message)
at System.ServiceModel.DiagnosticUtility.FailFast(String message)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.UninitializeCallContextCore(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.UninitializeCallContext(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Dispatch(MessageRpc& rpc, Boolean isOperationContextSet)
at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(IAsyncResult result)
at System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.Channels.InputQueue`1.AsyncQueueReader.Set(Item item)
at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(Item item, Boolean canDispatchOnThisThread)
at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(T item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)
at System.ServiceModel.Channels.InputQueueChannel`1.EnqueueAndDispatch(TDisposable item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)
at System.ServiceModel.Channels.SingletonChannelAcceptor`3.Enqueue(QueueItemType item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)
at System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, ItemDequeuedCallback callback)
at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContextCore(IAsyncResult result)
at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContext(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.ListenerAsyncResult.WaitCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
Process Name: Artech.Exception2CallContextInitializer.Services
Process ID: 7652
.NET Runtime version 2.0.50727.4927 - ICallContextInitializer.BeforeInvoke threw an exception of type System.Exception: 调用MyCallContextInitializer.AfterInvoke()出错!
如果你想从消息交换得角度进一步剖析问题的本质,你可以采用Fiddler这样的工具。如果你真的这样做的话,你会发现服务端没有任何消息返回到客户端。
二、原因剖析
从上面表现出来的现象,我们可以知道这是一个非常严重的问题,因为它将会终止整个服务宿主进程。那么,是什么导致了这个严重的问题呢?实际上,如果通过Reflector对WCF相关代码进行反射,你将会很容易找到问题的根源。
ICallContextInitializer的AfterInvoke方法的最终是通过定义在DispatchOperationRuntime类型的一个命名为UninitializeCallContextCore的私有方法中被调用的。下面就是该方法的定义:
1: private void UninitializeCallContextCore(ref MessageRpc rpc)
2: {
3: object proxy = rpc.Channel.Proxy;
4: int callContextCorrelationOffset = this.Parent.CallContextCorrelationOffset;
5: try
6: {
7: for (int i = this.CallContextInitializers.Length - 1; i >= 0; i--)
8: {
9: this.CallContextInitializers[i].AfterInvoke(rpc.Correlation[callContextCorrelationOffset + i]);
10: }
11: }
12: catch (Exception exception)
13: {
14: DiagnosticUtility.FailFast(string.Format(CultureInfo.InvariantCulture, "ICallContextInitializer.BeforeInvoke threw an exception of type {0}: {1}", new object[] { exception.GetType(), exception.Message }));
15: }
16: }
17:
18:
19:
20:
通过上面的代码,你会看到对DispatchOperation所有CallContextInitializer的AfterInvoke方法的调用是放在一个Try/Catch中进行的。当异常抛出后,会调用DiagnosticUtility的FailFast方法。传入该方法的是异常消息,你可以看到这里指定的消息是不对的,“ICallContextInitializer.BeforeInvoke”应该是“ICallContextInitializer.AfterInvoke”,这就是为什么你在Event Log看到日志内容是不准确的真正原因。我们进一步来看看FailFast的定义:
1: [MethodImpl(MethodImplOptions.NoInlining)]
2: internal static Exception FailFast(string message)
3: {
4: try
5: {
6: try
7: {
8: ExceptionUtility.TraceFailFast(message);
9: }
10: finally
11: {
12: Environment.FailFast(message);
13: }
14: }
15: catch
16: {
17: }
18: Environment.FailFast(message);
19: return null;
20: }
从上面的代码可以看到,整个过程分为两个步骤:对消息尽心Trace后调用Environment.FailFast方法。对 Environment.FailFast方法具有一定了解的人应该之后,该方法执行后会终止掉当前进程。这就是为什么在 ICallContextInitializer的AfterInvoke方法执行过程中出现未处理异常会导致宿主程序的非正常崩溃的真正原因。
三、总结
CallContextInitializer的设计可以看成是AOP在WCF中的实现,它可以在服务操作执行前后对方法调用进行拦截。你可以通过自定义CallContextInitializer实现一些服务操作执行前的初始化操作,以及操作执行后的清理工作。但是,当你自定义 CallContextInitializer的时候,一定要确保AfterInvoke方法中没有异常抛出来。