How can I directly craft the XML content that goes into a fault detail?
Getting control over the detail element doesn't have to mean crafting the fault message yourself. While WCF requires that the fault detail be serializable using a data contract, remember that DataContractSerializer treats XmlElement as a special primitive type. This allows you to construct arbitrary content using XmlElement when your content can be represented as a rooted document. Due to the automatic conversion process of FaultException to a fault message, you don't need to construct a data contract to act as a wrapper.
Here's a sample that builds some content in an XmlElement and uses it to construct a fault. I made the method return a Message so that I could look at the response more easily but you can use any contract you want.
[ServiceContract]public interface IMyService{ [OperationContract] Message Fail();}public class MyService : IMyService{ public Message Fail() { XmlDocument document = new XmlDocument(); XmlElement root = document.CreateElement("tag"); root.SetAttribute("attributeName", "value"); XmlElement subtag = document.CreateElement("moretags"); subtag.InnerText = "blah"; root.AppendChild(subtag); throw new FaultException<XmlElement>(root); }}public class Program{ static void Main(string[] args) { string address = "http://localhost:8000/"; BasicHttpBinding binding = new BasicHttpBinding(); ServiceHost host = new ServiceHost(typeof(MyService), new Uri(address)); host.AddServiceEndpoint(typeof(IMyService), binding, ""); host.Open(); ChannelFactory<IMyService> factory = new ChannelFactory<IMyService>(binding); IMyService proxy = factory.CreateChannel(new EndpointAddress(address)); Message response = proxy.Fail(); Console.WriteLine(response.ToString()); Console.ReadLine(); host.Close(); }}
That code produces a fault message that looks like the following.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Header /> <s:Body> <s:Fault> <faultcode>s:Client</faultcode> <faultstring xml:lang="en-US">The creator of this fault did not specify a Reason.</faultstring> <detail> <tag attributeName="value"> <moretags>blah</moretags> </tag> </detail> </s:Fault> </s:Body></s:Envelope>
Depending on how much hand-crafting you want, XmlDocument lets you simplify the process even further. For example, you could generate the same message from simple XML completely put together by hand.
XmlDocument document = new XmlDocument();document.LoadXml("<tag attributeName=\"value\"><moretags>blah</moretags></tag>");throw new FaultException<XmlElement>(document.FirstChild as XmlElement);
Next time: DataMember Best Practices