In my WCF client class I'm handling the Faulted()
event so that if the remote service throws an exception and faults the channel I can still at least shut it down gracefully. Here's my code:
protected void RemoteDataRetriever_Faulted(object sen开发者_C百科der, EventArgs e)
{
(sender as ICommunicationObject).Abort();
this.Dispose();
throw new ChannelTerminatedException("The remote service threw an unhandled exception and as a result the channel has been closed.");
}
So what I would expect is that the client can handle the ChannelTerminatedException
that I've thrown manually and send a message to the user, etc. Instead my exception is getting wrapped in a System.ServiceModel.Diagnostics.CallbackException
. Ok, fine. Except here's the catch: this CallbackException doesn't exist in the ServiceModel library and there appears to be no way for me to handle it except as a generic Exception
, which does me no good for my unit tests. What the heck is going on here? Can I disable that somehow and throw the exception that I originally wanted?
As it turns out, System.ServiceModel.Diagnostics.CallbackException
is an internal class stuffed inside a little known assembly called "%SystemRoot%\Microsoft.net\Framework\v3.0\Windows Communication Foundation\SMDiagnostics.dll", which itself contains only internal classes. Well, that stinks because it means that we can never, ever catch that exception. However, I was able to hunt down the class/method that instantiates the above exception (System.ServiceModel.Diagnostics.ExceptionUtility.ThrowHelperCallback(Exception innerException)) and found that it is being called by the virtual method OnFaulted() inside CommunicationObject. So theoretically any class that derives from CommunicationObject (sorry ClientBase<T>
) can override that method and tell it not to call ThrowHelperCallback(). Which means that the only viable candidates are classes that derive from ChannelFactoryBase<T>
. In theory I could go ahead and implement my own custom channel factory which suppresses the annoying CallbackException but at the moment it's too much work so I guess I'll just have to deal with it.
EDIT: @Jeremy - If I inspect the SOAP envelope coming back over the wire I find that it is giving me a generic fault, as expected, which indicates that the CallbackException is NOT being serialized and thus is NOT being generated on the server.
<s:Body>
<s:Fault>
<s:Code>
<s:Value>s:Receiver</s:Value>
<s:Subcode>
<s:Value xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault</s:Value>
</s:Subcode>
</s:Code>
<s:Reason>
<s:Text xml:lang="en-US">The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.</s:Text>
</s:Reason>
</s:Fault>
</s:Body>
You are seeing the System.ServiceModel.Diagnostics.CallbackException
because an exception is occurring on the server. As the exception is occurring outside of the domain of the client application, it is possibly of a type the client doesn't have access to. The callback mechanism handles this by generating the CallbackException you're seeing. This is similar to the System.TypeInitializationException
that gets thrown when an unhandled exception occurs when accessing a static member. If you're trying to handle this gracefully, you might want to handle the exception on the server side and close down the socket, which will subsequently trigger an exception on the client that can be handled.
精彩评论