Doc/literal Web services describe their input and output message formats entirely in terms of schema. When ASMX generates a WSDL definition for a [WebMethod], its default behavior is to create an element whose name reflects the name of the operation. Craig used this example:
[WebMethod]
[return: XmlElement("sum")]
public int Add(int x, int y) { return x + y; }
The schema definition of the request and messages for this operation looks like this:
<xs:element name="Add">
<xs:complexType>
<xs:sequence>
<xs:element name="x" type="xs:int" />
<xs:element name="y" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="AddResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="sum" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
The outer Add/AddResponse element is generated from the method name and it's content model is generated from the parameter list/return value. This type of service is described as doc/literal/wrapped because the operation name defines the outer wrapper element for both the request and response messages.
I never write my services this way.
Instead, I write services that are doc/literal/bare. Bare services differ from wrapped services in that the operation name has no impact on the structure of the message carried in an envelope's body. You make a service bare setting the [SoapDocumentMethod] attribute's ParameterStyle property to SoapParameterStyle.Bare. Here is an example:
public class Add
{
public int x;
public int y;
}
public class AddResponse
{
public int sum;
}
public class Math
{
[WebMethod]
[SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)]
[return: XmlElement("AddResponse")]
public AddResponse Add(Add req)
{
AddResponse resp = new AddResponse();
resp = req.x + req.y;
return resp;
}
}
In this case, the schemas for the input and output messages are generated entirely off the Add and AddResponse types. The Add method only binds the two together as the input/output of an operation in the generated WSDL portType. Here is a schema fragment for the messages:
<xs:element name="Add">
<xs:complexType>
<xs:sequence>
<xs:element name="x" type="xs:int" />
<xs:element name="y" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="AddResponse" type="tns:AddResponse" />
<xs:complexType name="AddResponse">
<xs:sequence>
<xs:element name="sum" type="xs:int" />
</xs:sequence>
</xs:complexType>
If you look closely, you'll see that these definitions are identical to the ones above (except for the use of an explicit named type for the response message, but that's irrelevant here.) So what is the point of introducing these "message types"?
1) They provide the most natural mapping to how XML messaging actually works. I design my services starting with XML and message exchanges, going to XSD and WSDL, then to code. This is the most natural projection into code that I could come up within, given the limitations of ASMX.
2) I now have a type that represents a message. This is better than a signature that represents a message because I can use it in other places. Perhaps I want to queue messages for async processing. Or store them in a file or a database. Or... your guess is as good as mine.
3) If I want to, I can process the entire message body as XML. This isn't possible with a wrapped service, where the dispatching process consumes the outer wrapper element before your code gets called.
4) I can build operations that accept multiple message formats by marking the single input or output argument with multiple [XmlElement] attributes and typing the argument as object or any common base type (or marking it with [XmlAnyElement] and typing it as XmlElement). This provides lots of flexibility. For instance, you could use it to handle two message formats representing different message versions with a breaking change (if it was a non-breaking change, you could model it with one message using a range of techniques I'll post about at some point.) Note that this technique doesn't work with the ASMX WSDL generator. In general, the dispatcher is smarter than the WSDL generator. If you are willing to write WSDL by hand, you gain a lot of freedom.
5) You can implement IXmlSerializable as a way to stream the body of your messages. This interface is even better in Whidbey.
6) You can completely decouple the action URI, the qname of the element in the body, and the operation name. This has two benefits. First, you can use one message format with multiple actions. For instance, a MathRequest message could work with both an Add and a Subtract action. (Richard noted in response to Craig's post that reliance on the soapAction HTTP header to identify the intent of your message was not a good idea. I agree: I use the wsa:Action SOAP header instead. WS-Addressing - or parts of it - really are just missing pieces from SOAP.) Second, operation name is no longer relevant. If you look closely at specs like WS-Eventing, you'll see that they describe the world solely in terms of message formats and action URIs, not WSDL operatio names (yes, there is a WSDL for WS-Eventing, but it's non-normative.) This degree of flexibility is very very useful.