Creating a Message


A Message object can be created via one of the numerous CreateMessage factory methods it defines. For the most part, these methods accept the content of the SOAP body as a parameter to the method. It is important to note that the body of a Message cannot be changed after it has been created. SOAP header blocks, on the other hand, can be added or changed after the Message has been created. Broadly speaking, the factory methods on the Message type are categorized as methods that populate the body of the Message by serializing a graph of objects, methods that pull data into a Message from an XmlReader, methods that push data into a Message, and methods that generate a Message representing a SOAP Fault. Before we examine the different categories of factory methods, let’s look at some of the context around Message serialization and deserialization.

A Word about Message Serialization and Deserialization

The words serialization and deserialization are common in distributed computing, and as a result, it is necessary to clarify their meaning as it relates to messaging applications. Let’s consider the basic serialization and deserialization steps when sending and receiving Message objects. When a sending application needs to send a Message to another messaging participant, it must first create a Message object that contains the appropriate information, then serialize and encode the contents of that Message to a Stream or Byte, and then transmit that Stream or Byte to the intended messaging participant. When a receiving application receives a Message, it is, for all practical purposes, in the form of a Stream or Byte (just as it left the sender). The receiving application must then decode and deserialize the Stream or Byte[] into a Message object and might need to optionally deserialize the contents of the header or the body of the Message into other objects.

As you can see, the serialization is generally associated with sender-specific tasks, and deserialization is generally associated with receiver-specific tasks. Both the sender and the receiver create Message objects, but the sender creates a Message from other objects in memory, while the receiver creates Message objects by decoding and deserializing a Stream or Byte into a Message. Once a receiving application decodes and deserializes a Stream or Byte into a Message, it can then transform the contents of the Message into another object, or a graph of objects. This transformation is, for all practical purposes, another deserialization step.

Message Versions

Because a Message object is the common language runtime abstraction of a SOAP message and there are multiple versions of SOAP in use, there is a need to express the SOAP version that a Message object is implementing. In the Message object model, the SOAP message version is applied when the Message object is created and cannot change afterward. The SOAP and WS-* specifications are living documents, and as a result, we should expect these documents to version over time. As they change, it is reasonable to assume that the qualifying namespaces and message structures they represent will change. To account for these inevitable changes, WCF provides several types that wrap SOAP-specific and WS-*-specific XML message semantics. Instances of these types are passed to the factory methods to indicate the intended SOAP version of the resultant Message object, and most of the factory methods defined on the Message type accept these types as parameters.

When applied to a Message, the System.ServiceModel.Channels.EnvelopeVersion type represents a SOAP specification that the Message will adhere to. Likewise, the System.ServiceModel. Channels.AddressingVersion type represents the WS-Addressing specification that the Message header blocks will adhere to when serialized. At the first release of WCF, there are two SOAP specifications (1.1 and 1.2) and two WS-Addressing specifications (August 2004 and 1.0).

The System.ServiceModel.Channels.MessageVersion type wraps both the EnvelopeVersion and the AddressingVersion types. MessageVersion has several static properties that represent the possible combinations of EnvelopeVersion and AddressingVersion. The following code snippet shows all of the publicly visible members of the MessageVersion type:

 namespace System.ServiceModel.Channels {   public sealed class MessageVersion {     public AddressingVersion Addressing { get; }     public static MessageVersion Default { get; }     public EnvelopeVersion Envelope { get; }     public static MessageVersion Soap11 { get; }     public static MessageVersion Soap12 { get; }     public static MessageVersion None { get; }     public static MessageVersion Soap11WSAddressing10 { get; }     public static MessageVersion Soap11WSAddressingAugust2004 { get; }     public static MessageVersion Soap12WSAddressing10 { get; }     public static MessageVersion Soap12WSAddressingAugust2004 { get; }     public static MessageVersion CreateVersion(       EnvelopeVersion envelopeVersion);     public static MessageVersion CreateVersion(       EnvelopeVersion envelopeVersion,       AddressingVersion addressingVersion);     public override bool Equals(object obj);     public override string ToString();   } }

Most of these members are self-explanatory; a few require some explanation. In the version 3 release of WCF, the MessageVersion.Default property returns the equivalent of the MessageVersion.Soap12WSAddressing10 static property. As you can see from the name, this property prepresents the infrastructure compliant with the SOAP 1.2 and WS-Addressing 1.0 specifications. The MessageVersion.Soap11 and MessageVersion.Soap12 properties set the AddressingVersion to AddressingVersion.None and set the EnvelopeVersion according to the name of the property. This is useful if you are creating a messaging application that needs to send SOAP messages but that does not implement WS-Addressing. The MessageVersion.None property returns a MessageVersion that indicates EnvelopeVersion.None and AddressingVersion.None. As you might expect, MessageVersion.None is useful in POX messaging scenarios.

Important  

When either of these specifications evolve (and they inevitably will), code that builds a Message with the MessageVersion.Default might silently change in subsequent versions of WCF. This can be good or bad, depending on the messaging scenario. For example, upgrading to a future version of WCF might update the XML plumbing generated by the MessageVersion.Default property. If this update occurs, all of the messaging participants that interact with the updated message must understand the new message semantics. If all participants upgrade simultaneously, there shouldn’t be any problems. If, however, we want to achieve independence in our versioning practices, accommodations must be made to interact with both the old and the new messages. In other words, an application that uses the MessageVersion.Default property might very well create a breaking change simply by upgrading WCF, and as a result, the MessageVersion.Default property should be used with caution.

The following code shows the the differences in the SOAP and WS-Addressing versions referenced by these properties of the MessageVersion type:

 using System; using System.ServiceModel.Channels; using System.Xml; class Program {   static void Main(string[] args){     MessageVersion version = MessageVersion.Default;     PrintMessageVersion("Default",version);     version = MessageVersion.Soap11WSAddressing10;     PrintMessageVersion("Soap11WSAddressing10",version);     version = MessageVersion.Soap11WSAddressingAugust2004;     PrintMessageVersion("Soap11WSAddressingAugust2004",version);     version = MessageVersion.Soap12WSAddressing10;     PrintMessageVersion("Soap12WSAddressing10",version);     version = MessageVersion.Soap12WSAddressingAugust2004;     PrintMessageVersion("Soap12WSAddressingAugust2004", version);   }   private static void PrintMessageVersion(String name,                                           MessageVersion version) {     Console.WriteLine("Name={0}\nEnvelope={1}\nAddressing={2}\n",                        name,                        version.Envelope.ToString(),                        version.Addressing.ToString());   } }

When this code runs, it generates the following output:

 Name=Default Envelope=Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing=Addressing10 (http://www.w3.org/2005/08/addressing) Name=Soap11WSAddressing10 Envelope=Soap11 (http://schemas.xmlsoap.org/soap/envelope/) Addressing=Addressing10 (http://www.w3.org/2005/08/addressing) Name=Soap11WSAddressingAugust2004 Envelope=Soap11 (http://schemas.xmlsoap.org/soap/envelope/) Addressing=Addressing200408 (http://schemas.xmlsoap.org/ws/2004/08/addressing) Name=Soap12WSAddressing10 Envelope=Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing=Addressing10 (http://www.w3.org/2005/08/addressing) Name=Soap12WSAddressingAugust2004 Envelope=Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing=Addressing200408   (http://schemas.xmlsoap.org/ws/2004/08/addressing)

Serializing an Object Graph

Several of the CreateMessage methods are designed to serialize an Object graph into the body of a Message. To that end, these methods accept a parameter of type System.Object. One of these methods uses the default WCF serializer, and another accepts a custom serializer as a parameter. (We will examine serialization in more detail in Chapter 9, “Contracts.”) In addition to these parameters, these methods accept a parameter of type String. This parameter sets the value of the WS-Addressing Action header block in the resultant Message object. As shown here, these factory methods are fairly straightforward:

 // pass a String into the factory method Message msg = Message.CreateMessage(MessageVersion.Soap12WSAddressing10,                                     "urn:SomeAction",                                     "Hello There"); // the ToString() method returns the entire Message Console.WriteLine(msg.ToString());

When this code executes, the following output is generated:

 <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing"     xmlns:s="http://www.w3.org/2003/05/soap-envelope">   <s:Header>     <a:Action s:mustUnderstand="1">urn:SomeAction</a:Action>   </s:Header>   <s:Body>     <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">       Hello There     </string>   </s:Body> </s:Envelope>

As you can see from the preceding example, the String “Hello There” was automatically assigned to the body of the Message. Let’s change the Object parameter from a String to a PurchaseOrder, as shown in the following code snippet, and see what happens:

 sealed class MyApp {   static void Main() {     VendorInfo vInfo = new VendorInfo(5, "Contoso");     PurchaseOrder po = new PurchaseOrder(50, vInfo);     Message msg = Message.CreateMessage(       MessageVersion.Soap12WSAddressing10,       "urn:SomeAction",       po);     // the ToString() method returns the entire Message     Console.WriteLine(msg.ToString());   }   private sealed class PurchaseOrder {     Int32 poNumber;     VendorInfo vendorInfo;     internal PurchaseOrder(Int32 poNumber, VendorInfo vendorInfo) {       this.poNumber = poNumber;       this.vendorInfo = vendorInfo;     }   }   private sealed class VendorInfo {     Int32 vendorNumber;     String vendorName;     internal VendorInfo(Int32 vendorNumber, String vendorName) {       this.vendorNumber = vendorNumber;       this.vendorName = vendorName;     }   } }

When this code runs, the default serializer used by the CreateMessage method throws an InvalidDataContractException. We will examine data contracts and serialization in more detail in Chapter 9. If we want to pass an object graph to these methods, it must be serializable, and all the objects it refers to must also be serializable. The first example of passing a String to the CreateMessage method succeeded because C# primitive types are implicitly serializable. There are many types that are implicitly serializable, and there are several ways to make a type explicitly serializable. You will learn more about implicit and explicit serialization in Chapter 9. For the moment, let’s annotate both the PurchaseOrder and VendorInfo types with the SerializableAttribute attribute, thereby making them serializable. If we run the preceding example with serializable types, we see the following output:

 <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing"     xmlns:s="http://www.w3.org/2003/05/soap-envelope">   <s:Header>     <a:Action s:mustUnderstand="1">urn:SomeAction</a:Action>   </s:Header>   <s:Body>     <MyApp.PurchaseOrder       xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns= "http://schemas.datacontract.org/2004/07/CreatingMessageBySerialization">       <poNumber>50</poNumber>       <vendorInfo>         <vendorName>Contoso</vendorName>         <vendorNumber>5</vendorNumber>       </vendorInfo>     </MyApp.PurchaseOrder>   </s:Body> </s:Envelope>

Notice that a PurchaseOrder object (and a VendorInfo object) are serialized to the body of the SOAP message.

Pulling Data from a Reader

Several of the CreateMessage methods accept either an XmlReader or an XmlDictionaryReader. These methods “pull” either the entire contents of the XmlDictionaryReader into the returned Message or the contents of the XmlDictionaryReader into the body of the Message. It is important to note that the CreateMessage methods that accept an XmlReader as a parameter create an XmlDictionaryReader object by calling the CreateDictionaryReader factory method on the XmlDictionaryReader type.

These methods are most useful when you need to deserialize a Message from a Byte[] or a Stream, as is the case when a receiving application receives a Stream that contains a serialized and encoded Message. When building a Message using one of these methods, you must know whether the underlying Byte[] or Stream includes the entire contents of a Message or just the body. To accommodate both scenarios, CreateMessage is overloaded to include parameters that read the entire envelope and parameters that read just the body element.

To illustrate, the following file contains the contents of a message that has been serialized to a file named entireMessage.xml:

 <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/ 05/soap-envelope">   <s:Header>     <a:Action s:mustUnderstand="1">urn:SomeAction</a:Action>   </s:Header>   <s:Body>     <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">       Hello Message     </string>   </s:Body> </s:Envelope>

Likewise, the following is a file named bodyContent.xml that contains the body of a message:

 <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">   Hello Message </string>

The following code sample shows how to build messages using both of these sources:

 const Int32 MAXHEADERSIZE = 500; // ENVELOPE READER EXAMPLE // Get data from the file that contains the entire message FileStream stream = File.Open("entireMessage.xml", FileMode.Open); XmlDictionaryReader envelopeReader =     XmlDictionaryReader.CreateTextReader(stream, new       XmlDictionaryReaderQuotas()); Message msg = Message.CreateMessage(envelopeReader,                                     MAXHEADERSIZE,                                     MessageVersion.Soap12WSAddressing10); Console.WriteLine("{0}\n", msg.ToString()); //BODY READER EXAMPLE // Get data from a file that contains just the body stream = File.Open("bodyContent.xml", FileMode.Open); XmlDictionaryReader bodyReader =     XmlDictionaryReader.CreateTextReader(stream, new       XmlDictionaryReaderQuotas()); msg = Message.CreateMessage(MessageVersion.Soap12WSAddressing10,                             "urn:SomeAction", bodyReader); Console.WriteLine("{0}\n", msg.ToString());

Notice that the first call to CreateMessage accepts an Int32 as a parameter that indicates the maximum size in bytes of the Message header. This limit is necessary because we are deserializing the entire contents of the Message from a Stream. Since the header of a Message is always buffered, this parameter allows us to control the size of that buffer, and the resource demands placed on the application as that Message is processed. Because the second call to CreateMessage is reading only the contents of the body from the Stream, control over the size of the header buffer is not necessary.

Pushing Data into a Message with a BodyWriter

One of the CreateMessage overloads allows callers to “push” data into the Message by means of a System.ServiceModel.Channels.BodyWriter. A BodyWriter is an abstract type that exposes a protected abstract method named OnWriteBodyContents that accepts an XmlDictionaryWriter as a parameter. It is through this method that a BodyWriter derived type can exert control over the creation of the body of a Message, and therefore, a BodyWriter is useful for exerting control over Message deserialization. For the most part, the implementation of the OnWriteBodyContents method consists of calling various Write methods on the XmlDictionaryWriter parameter. The following example illustrates a BodyWriter derived type that is intended to read the contents of an XML file and push the contents of the file into the body of a Message:

 sealed class MyBodyWriter : BodyWriter {   private String m_fileName;   internal MyBodyWriter(String fileName) : base(true) {     this.m_fileName = fileName;   }   protected override void OnWriteBodyContents(XmlDictionaryWriter writer) {     using (FileStream stream = File.Open(m_fileName, FileMode.Open)) {       XmlDictionaryReader reader1 =         XmlDictionaryReader.CreateTextReader(stream, new           XmlDictionaryReaderQuotas());       reader1.ReadStartElement();       while (reader1.NodeType != XmlNodeType.EndElement) {         writer.WriteNode(reader1, true);       }     }   } }

Once the BodyWriter is subclassed, it can be used in a CreateMessage method, as shown here:

 Message pushMessage = Message.CreateMessage(                         MessageVersion.Soap12WSAddressing10,                         "urn:SomeAction",                         new MyBodyWriter("bodyContent.xml"));

Messages and SOAP Faults

The Message type defines a few factory methods that create Message objects that represent a SOAP Fault. A SOAP Fault is a form of a SOAP message that carries error information. It is distinct from other SOAP messages in that the SOAP specifications (both 1.1 and 1.2) dictate the content of the body, and in some cases, a few of the header blocks of the SOAP message. By virtue of the fact that a Message is the common language runtime abstraction of a SOAP message, a Message can represent a SOAP Fault, just as it can represent a SOAP message. This section describes some of the basics of SOAP Faults, the types fundamental in creating a Message that represents a SOAP Fault, and how to create a Message that represents a SOAP fault.

SOAP Fault Anatomy

SOAP Fault anatomy is dictated by SOAP specifications (1.1 and 1.2). Fundamentally, a SOAP 1.1 Fault contains a SOAP body that wraps a mandatory faultcode element, a mandatory faultstring element, an optional faultactor element, and an optional faultdetail element. To avoid repeating the specification here, see http://www.w3.org/TR/soap11 for more information about the rules that dictate when the optional elements should appear. At a high level, the faultcode element represents an identifier that can be used by the sender and receiver infrastructures to identify the type of error that occurred. The SOAP 1.1 specification defines a small set of faultcodes, but an application is free to define faultcodes that are unique to an application. The faultstring element is intended to be a human-readable representation of the faultcode and is not intended to be used by the receiving application (unless the contents of the faultstring are shown to the user). The faultactor element is a URI that describes the the source of the error.

The structure of a SOAP Fault changes dramatically from SOAP 1.1 to SOAP 1.2. Because SOAP 1.2 is built on the Infoset, a SOAP 1.2 Fault is fundamentally composed of a set of information items. In addition to this fundamental change in the representation of a SOAP Fault, the names of the parts of a SOAP Fault have been changed and expanded to include more descriptive information. SOAP 1.2 states that a SOAP Fault should contain a mandatory Code information item, a mandatory Reason information item, an optional Node information item, an optional Role information item, and an optional Detail information item. Information about the rules surrounding when an information item is needed can be found at http://www.w3.org/TR/soap12-part1/#soapfault. In general, the Code information item represents an identifier of the error that occurred, and allows nesting sub-Code information items to provide more granular information about the error. SOAP 1.2 defines a few Code information items and allows an application to define its own values. The Reason information item represents a human-readable explanation of the error. The Node information item represents the messaging participant that caused the SOAP Fault. The Role information item represents the SOAP Role that the messaging participant was participating in when the SOAP Fault was generated. The Detail information item is intended to be a bucket for other relevant information about the error.

SOAP 1.1 and 1.2 Faults, despite their differences, are similar in the type of information they describe. Both of these specifications define placeholders for an error code, a human-readable description of the error, a description of the messaging participant that caused the SOAP Fault, and a bucket that contains extra information about the error. To this end, WCF defines a type named System.ServiceModel.Channels.MessageFault that represents the information stored in SOAP 1.1 and SOAP 1.2 Faults. Before we look at how to express a SOAP Fault in a format described by SOAP 1.1 and SOAP 1.2, let’s first examine how to generalize a SOAP Fault through the MessageFault type.

The MessageFault Type

The MessageFault type is a way to describe error information in a SOAP-version-agnostic manner. Keeping in mind that WCF has a highly layered architecture, the MessageFault type provides tremendous flexibility when processing SOAP messages and optionally generating exceptions.

Creating a MessageFault Object   Like many other types in WCF, MessageFault is an abstract type that defines several factory methods. These factory methods accept parameters that represent the information stored in a SOAP Fault. In addition to these parameters, the MessageFault also defines factory methods that accept a parameter identifying the messaging participant generating the SOAP Fault. It is worth noting that the MessageFault type defines one factory method that accepts a Message as a parameter. This method is quite useful when a WCF application receives a Message determined to be a SOAP Fault and needs to pass information about that fault to other parts of the WCF infrastructure for processing.

Information about the faultcode is represented by the System.ServiceModel.FaultCode type. This type defines constructors as well as factory methods. All of these creational methods allow the specification of a sub-Code. The factory methods on the FaultCode type, however, automate the generation of sender and receiver fault codes (as defined in both SOAP 1.1 and SOAP 1.2).

Information about the faultreason is represented by the System.ServiceModel.FaultReason type. In the simplest case, one constructor accepts a parameter of type String, where the String represents human-readable information about the error. Since humans do not all speak the same language (even Microsoft .NET developers can’t agree on a language), the FaultReason type defines constructors and methods that allow an application to embed multiple translations of a String in the FaultReason and extract the appropriate String based on a CultureInfo.

All but one of the factory methods defined on the MessageFault type accept parameters of type FaultCode and FaultReason. As a result, these types must be instantiated before a MessageFault is created, except when creating a MessageFault from a Message. Several of the factory methods also accept an Object as a parameter, and this parameter represents extra information about the error. As with the Object parameter in the factory methods on the Message type, the type passed for this parameter must be serializable (more on serialization in Chapter 9). The existence of this parameter begs the question, “What type of object should I use for this parameter?” Since System.Exception is serializable, you might be tempted to pass an Exception for this parameter. I strongly encourage you to resist this temptation. I prefer creating a custom type whose purpose is passing error information to other messaging participants. As we will see in Chapter 9, this demands a change to the contract.

Creating a Message from a MessageFault   Once we have created a MessageFault, we can create a Message from it by calling one of the factory methods defined on the Message type. The following code snippet demonstrates how to use the FaultCode, the FaultReason, and an Object to create a MessageFault, as well as how to build a Message object from a MessageFault object:

 static void Main() {   // create a Receiver Fault Code   FaultCode faultCode = FaultCode.CreateReceiverFaultCode("MyFaultCode",                                                           "urn:MyNS");   // create a meaningful FaultReason   FaultReason faultReason = new FaultReason("The value must be > 10");   // create an object that represents the SOAP Fault detail   SomeFaultDetail faultDetail = new SomeFaultDetail("Contoso", "SomeApp");   // create a MessageFault   MessageFault messageFault = MessageFault.CreateFault(faultCode,                                                        faultReason,                                                        faultDetail);   // Build a Message from the MessageFault, passing the MessageVersion   CreateAndShowMessage(messageFault, MessageVersion.Soap11WSAddressing10);   CreateAndShowMessage(messageFault, MessageVersion.Soap12WSAddressing10); } private static void CreateAndShowMessage(MessageFault messageFault,                                          MessageVersion version) {   // actually create the Message object w/version info   Message message = Message.CreateMessage(version,                               messageFault,                                           "urn:SomeFaultAction");   // show the contents of the Message   Console.WriteLine("{0}\n", message.ToString()); } // a serializable type for storing Fault detail information [Serializable] sealed class SomeFaultDetail {   String companyName;   String applicationName;   DateTime? dateOccurred;   internal SomeFaultDetail(String companyName, String applicationName) {     this.companyName = companyName;     this.applicationName = applicationName;     //this.dateOccurred = null;     this.dateOccurred = DateTime.Now;   } }

When this code executes, the following output is generated:

 <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing"     xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">   <s:Header>     <a:Action s:mustUnderstand="1">       urn:SomeFaultAction     </a:Action>   </s:Header>   <s:Body>     <s:Fault>       <faultcode xmlns:a="urn:MyNS">a:MyFaultCode</faultcode>       <faultstring xml:lang="en-US">The value must be &gt; 10</faultstring>       <detail>         <Program.SomeFaultDetail xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MessageFaults">           <applicationName>SomeApp</applicationName>           <companyName>Contoso</companyName>           <dateOccurred>2006-06-14T12:34:44.52325-04:00</dateOccurred>         </Program.SomeFaultDetail>       </detail>     </s:Fault>   </s:Body> </s:Envelope> <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing"     xmlns:s="http://www.w3.org/2003/05/soap-envelope">   <s:Header>     <a:Action s:mustUnderstand="1">       urn:SomeFaultAction     </a:Action>   </s:Header>   <s:Body>     <s:Fault>       <s:Code>         <s:Value>s:Receiver</s:Value>         <s:Subcode>           <s:Value xmlns:a="urn:MyNS">a:MyFaultCode</s:Value>         </s:Subcode>       </s:Code>       <s:Reason>         <s:Text xml:lang="en-US">The value must be &gt; 10</s:Text>       </s:Reason>       <s:Detail>         <Program.SomeFaultDetail xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MessageFaults">           <applicationName>SomeApp</applicationName>           <companyName>Contoso</companyName>           <dateOccurred>2006-06-14T12:34:44.52325-04:00</dateOccurred>         </Program.SomeFaultDetail>       </s:Detail>     </s:Fault>   </s:Body> </s:Envelope>

The most striking feature this code snippet shows is how the MessageFault is truly SOAP version agnostic. The first call to CreateAndShowMessage passes the MessageFault and MessageVersion.Soap11WSAddressing10 as parameters, and the result is a SOAP 1.1 Fault. The second call to CreateAndShowMessage passes the same MessageFault but changes the MessageVersion to MessageVersion.Soap12WSAddressing10. The result is a SOAP 1.2 Fault.

The preceding example also shows how to create a Message from a MessageFault. The Message type defines a factory method that accepts a MessageFault and several others that accept a FaultCode. These factory methods on the Message type allow an application to create a MessageFault or FaultCode indicating an error and then pass that object to another layer in the WCF infrastructure to generate a Message object.

Note 

This might seem like a subtle capability, but it provides tremendous benefit. In effect, the MessageVersion-agnostic capability of the MessageFault type allows the SOAP version decision to be deferred to another part of the WCF infrastructure. In other words, only one layer in the WCF infrastructure needs to know the SOAP version required for transmission, thereby creating a more pluggable and extensible framework.




Inside Windows Communication Foundation
Inside Windows Communication Foundation (Pro Developer)
ISBN: 0735623066
EAN: 2147483647
Year: 2007
Pages: 106
Authors: Justin Smith

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net