This should be pretty short, but I could not find this information anywhere so here's something that will save you hours of frustration if you ever run into the same situation.
Windows Communication Foundation is nice and all, but it suffers from a really large myopia: it tends to assume that you control both the service and the client. This is a stupid assumption, but try finding information for a situation where a service throws a FaultException and you want to get at the details of the exception and you'll see what I mean.
Dynamics Great Plains Web Services allow you to manipulate common business objects. I've recently been trying to use WCF in communicating with those services and it has been an adventure. A recent post detailed my solution for customized warehousing. What I left out last time is moving to WCF's error handling seriously messed up my error condition reporting.
GP Web Services sends a fault for both warnings and errors with a relatively informative status message and a detail section that is just a GUID that identifies the fault in the server log. The theory is that if you want more details, you can query the service again to pull further information in.
All the solutions I could find for getting fault details out of WCF involve creating contracts on the service that the WCF client can read to create strongly typed objects in the generic version of FaultException. Well, I can't control the web service to do this. I could probably have hacked or wrapped the wsdl for the service but who wants that much low-level headache? And before you go there, the faults that the GP Web Service sends didn't trigger on what was billed as the default exception with generic details:
catch (FaultException<ExceptionDetail> ex)
Unfortunately, the regular FaultException doesn't expose a Detail property or methods that you can use to get at any details sent. This is a stupid oversight. I mean, yeah, anything can be in there, but whatever is there has to be XML serializable pretty much by definition. They couldn't throw us a bone and expose it as a string or XML fragment?
So what do you do with WCF if you cannot change the service but need to get at a fault detail without a specific .Net-friendly contract?
The solution I came up with is actually extremely easy once I realized I had to go through a couple of classes to get there (don't ask how long this took to ferret out).
- Snag the message itself using MessageFault.
- Get an XmlDictionaryReader that can get at the contents of the details sent.
- Read the detail content.
Since I know that the detail content is always a GUID, this is really simple:
catch (FaultException soapEx)
MessageFault mf = soapEx.CreateMessageFault();
XmlDictionaryReader reader = mf.GetReaderAtDetailContents();
Guid g = reader.ReadContentAsGuid();
Amazingly, this works.