There are four cases that I think are interesting to look at so that you can see the different fault behaviors that could occur.
Untyped fault exception with an untyped message contract
The basic case from yesterday is to receive a fault message.
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"> <s:Header> <a:Action s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/soap/fault</a:Action> <a:RelatesTo>urn:uuid:dd129ffe-a8ff-4a70-ad6f-ad48085e94e8</a:RelatesTo> <a:To s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/anonymous</a:To> </s:Header> <s:Body> <s:Fault> <s:Code> <s:Value>s:Sender</s:Value> </s:Code> <s:Reason> <s:Text xml:lang="en-US">boo!</s:Text> </s:Reason> </s:Fault> </s:Body></s:Envelope>
Typed fault exception with an untyped message contract
I'm just changing the FaultException to a FaultException<string> here, although you can have any type you want for the fault detail. This changes the contents of the fault message but not the code path. Note that the action is different in addition to the detail section to match the parameterized type.
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"> <s:Header> <a:Action s:mustUnderstand="1">http://tempuri.org/IService/VerbStringFault</a:Action> <a:RelatesTo>urn:uuid:49ee87c7-691f-48c4-86ea-bb172c99294d</a:RelatesTo> <a:To s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/anonymous</a:To> </s:Header> <s:Body> <s:Fault> <s:Code> <s:Value>s:Sender</s:Value> </s:Code> <s:Reason> <s:Text xml:lang="en-US">The creator of this fault did not specify a Reason.</s:Text> </s:Reason> <s:Detail> <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">boo!</string> </s:Detail> </s:Fault> </s:Body></s:Envelope>
Untyped fault exception with a typed message contract
As soon as we lose the untyped message (any return type but Message, even void), then we get an entirely different code path on the client. Instead of a return value, an exception of type FaultException gets thrown from the proxy.
System.ServiceModel.FaultException: boo!Server stack trace: at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc) at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout) at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs) at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation) at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) at IService.Verb(Message input)
Typed fault exception with a typed message contract
With typed messages, the fault exception type is now important for error handling. The parameterized server FaultException comes to the client as the same parameterized type (so FaultException), assuming that we've set up the fault contract for the service correctly. This means that we have the same basic code path but may be selecting a different case to apply.
System.ServiceModel.FaultException`1[System.String]: The creator of this fault did not specify a Reason. (Fault Detail is equal to boo!).
Next time: Flow of Messages, Part 1
What does this code print? It seems like both choices are quite reasonable. I'll have some discussion
Harry wonders if it has been a slow week. It started that way for me but its certainly not now with both