The XML Files

Messages vs. Methods

Aaron Skonnard

Q What happens behind the scenes when you call a method on a Web service proxy object?

Q What happens behind the scenes when you call a method on a Web service proxy object?

A Given that most Web services toolkits make heavy use of object-oriented abstractions, it's easy to ignore what's going on behind the scenes when interacting with a service through a generated proxy class. The Microsoft® .NET Framework makes it possible to generate proxy classes from Web Services Description Language (WSDL) definitions by running wsdl.exe on the command line or by selecting "Add Web Reference" from within Visual Studio® .NET. Then you can take the generated proxy class and simply make method calls in order to invoke operations on the Web service.

A Given that most Web services toolkits make heavy use of object-oriented abstractions, it's easy to ignore what's going on behind the scenes when interacting with a service through a generated proxy class. The Microsoft® .NET Framework makes it possible to generate proxy classes from Web Services Description Language (WSDL) definitions by running wsdl.exe on the command line or by selecting "Add Web Reference" from within Visual Studio® .NET. Then you can take the generated proxy class and simply make method calls in order to invoke operations on the Web service.

For example, the following code illustrates how to interact with a Web service that exposes the canonical Add operation:

MathService s = new MathService(); double sum = s.Add(3, 4);

This type of framework really simplifies the act of working with Web services for those already familiar with object-oriented programming. The generated MathService class represents the Web service endpoint of interest, but it's different from a traditional .NET class because the implementation of the Add method (or any method for that matter) isn't found on the object itself, but rather across the Internet on a different machine. At first this may feel a lot like using distributed objects in COM+, but it's actually quite different because you don't have to use objects on either side.

Web services are built on the foundations of XML messaging. You send a SOAP message to a Web service to invoke one of its operations. And if it's a request/response operation, the Web service will then send another SOAP message back to the original sender. The ability to work with a Web service through an object is simply a facade that hides the underlying XML and protocol details.

When you instantiate the MathService class I just discussed, you get a local object that serves as a gateway (or proxy) for future Web service invocations. The MathService object does not get instantiated on another machine like it would in COM+. In fact, nothing happens on the wire at this point.

The magic begins when you call one of the methods on the local object, like Add. Since the Web service resides on another machine, the implementation of Add must generate the appropriate SOAP message to send across the wire. It does this by using .NET reflection to inspect the method signature and any applied attributes that govern the underlying XML details.

Once it has generated the SOAP message, the proxy opens an HTTP connection and transmits the SOAP message. Upon receiving the message, the Web service endpoint will process the "Add" functionality using the supplied data. And in this case, the service will return a response SOAP message containing the result. The proxy waits for the HTTP response and then processes the returned SOAP message, mapping it back to the appropriate .NET type for the return value.

For the most part, the XML and HTTP details are hidden behind the proxy's object facade. It can't hide everything, however, because there are some things that you may need to control, such as location, authentication, or proxy server details. The generated proxy class, MathService, derives from a base class called SoapHttpClientProtocol, which exposes several properties for controlling some of these underlying details. For example, you can modify the URL used to establish the HTTP connection through the proxy's Url property, as shown here:

MathService s = new MathService(); s.Url = "https://skonnard.com/samples/sample.asmx"; double sum = s.Add(3, 4);

Or you might need to set the HTTP basic authentication credentials through the proxy's Credentials property:

MathService s = new MathService(); s.Credentials = new NetworkCredential("bob", "iL0veCheEse!"); double sum = s.Add(3, 4);

As you can see, the proxy object is responsible for performing the translation between the different worlds of objects and XML, and for transmitting the XML messages to the actual Web service endpoint. It does a good job of hiding most of these details but sometimes you have to peer through the veil and tweak things to achieve the exact functionality you need.

Q Is it possible to pass parameters by reference to a WebMethod?

Q Is it possible to pass parameters by reference to a WebMethod?

A Passing parameters by reference is a programming concept found in most of today's object-oriented languages. For example, in C# you annotate a parameter with the ref modifier to indicate that it's a reference parameter. This means the parameter will not contain a copy of the supplied value (with a new storage location), but rather a reference to the same storage location used by the variable in the calling code. So if you change the value of the reference parameter within the method, the change will be reflected in the original variable after returning from the method.

A Passing parameters by reference is a programming concept found in most of today's object-oriented languages. For example, in C# you annotate a parameter with the ref modifier to indicate that it's a reference parameter. This means the parameter will not contain a copy of the supplied value (with a new storage location), but rather a reference to the same storage location used by the variable in the calling code. So if you change the value of the reference parameter within the method, the change will be reflected in the original variable after returning from the method.

Mapping this concept to Web services presents a challenge because you're not dealing with memory storage locations anymore. Web service invocations typically span process, machine, or organizational boundaries. When you invoke a Web service operation, you're ultimately generating a SOAP message that's transmitted across the wire to another application. The service then returns a response through another SOAP message.

Obviously this means that the Web service cannot have direct access to the caller's memory locations. However, it is possible to simulate the passing of parameters by reference in a distributed environment (for example, Web services, remoting, COM+, and so on) by returning "reference parameter" values back in the response message. The calling code can then update the original variables with any changes.

The WebMethod framework supports this approach by allowing you to use the ref modifier in your WebMethod signatures. Doing so only has an effect on the response message, which will then include the reference parameters. For example, consider a WebMethod that does not contain any reference parameters:

[WebMethod] public double Add(double x, double y) { return x + y; }

If you invoke this operation, supplying the value of 3 for x and y, you'll get the following SOAP response message:

<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <AddResponse xmlns="https://example.org/sample"> <AddResult>6</AddResult> </AddResponse> </soap:Body> </soap:Envelope>

Let's modify the signature to make x a reference parameter and change the implementation to increment x's value before returning:

[WebMethod] public double Add(ref double x, double y) { return x++ + y; }

Now when you invoke this operation using the same values as before, you'll get the following SOAP response message:

<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <AddResponse xmlns="https://example.org/sample"> <AddResult>6</AddResult> <x>4</x> </AddResponse> </soap:Body> </soap:Envelope>

Notice that x is returned in the response message along with the result. The WSDL file generated by the WebMethod framework contains the XML Schema definition (shown in Figure 1) to describe the elements found in the request and response messages.

Figure 1 XML Schema Definition

<s:schema elementFormDefault="qualified" targetNamespace="https://ex_mple.org/sample"> <s:element name="Add"> <s:complexType> <s:sequence> <s:element name="x" type="s:double" /> <s:element name="y" type="s:double" /> </s:sequence> </s:complexType> </s:element> <s:element name="AddResponse"> <s:complexType> <s:sequence> <s:element name="AddResult" type="s:double" /> <s:element name="x" type="s:double" /> </s:sequence> </s:complexType> </s:element> </s:schema>

When you generate a proxy class from this WSDL definition, the .NET Framework is smart enough to figure out that since x is found in both the request and response messages, it can be represented in C# as a reference parameter in the generated proxy class. So the generated proxy class in this case will contain a method that looks like the following (attributes omitted):

public double Add(ref double x, double y) { object[] results = this.Invoke("Add", new object[] { x, y}); x = ((double)(results[1])); return ((double)(results[0])); }

To invoke this operation through the proxy class, you must now specify that it's a reference parameter when making the call. This ensures that you provide a variable that can contain the updated value after the call returns, as illustrated in the following example:

SampleService s = new SampleService(); double x = 3; Console.WriteLine("sum={0}", s.Add(ref x, 4)); Console.WriteLine("x={0}", x);

Running this code outputs "sum=7" and "x=4", showing that the updated x value made its way back to the original variable.

Q I'm having a problem passing a linked list to a WebMethod by reference. I have the following class:

public class Widget { public string Name; public Widget NextWidget; }

Q I'm having a problem passing a linked list to a WebMethod by reference. I have the following class:

public class Widget { public string Name; public Widget NextWidget; }

I've defined a WebMethod that receives a Widget by reference. The implementation traverses the list and changes the name of each Widget to "MyWidget", as shown here:

[WebMethod] public void Test (ref Widget widget) { Widget next = widget; while (next != null) { next.Name = "MyWidget"; next = next.NextWidget; } }

On the client side, I run the code shown in Figure 2. When the call returns, only the first Widget's Name field has been set. What am I missing?

Figure 2 Widget WebMethod

Widget w1 = new Widget(); Widget w2 = new Widget(); Widget w3 = new Widget(); w1.NextWidget = w2; w2.NextWidget = w3; w3.NextWidget = null; WidgetService ws = new WidgetService(); ws.Test (ref w1); Console.WriteLine(w1.Name); Console.WriteLine(w2.Name); Console.WriteLine(w3.Name);

A This problem, like many others, stems from the fact that you're thinking that Web services can be treated like traditional object systems in which code like this might work. In this example, you're passing w1 by reference to the Test operation. Widget w1 references w2, which references w3. However, when you invoke the Test operation, the object graph is serialized into the SOAP request message shown in Figure 3.

A This problem, like many others, stems from the fact that you're thinking that Web services can be treated like traditional object systems in which code like this might work. In this example, you're passing w1 by reference to the Test operation. Widget w1 references w2, which references w3. However, when you invoke the Test operation, the object graph is serialized into the SOAP request message shown in Figure 3.

Figure 3 SOAP Request Message

<soap:Envelope xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <Test xmlns="https://example.org/sample"> <widget> <Name></Name> <NextWidget> <Name></Name> <NextWidget> <Name></Name> <NextWidget xsi:nil="true" /> </NextWidget> </NextWidget> </widget> </Test> </soap:Body>

The WebMethod will create a similar object graph when it deserializes this message, although it will be referenced by the parameter named widget instead of w1. The code within the WebMethod will then traverse the list of widgets and update the name of each one. The SOAP message returned from the WebMethod will now look like Figure 4.

Figure 4 SOAP Response Message

<soap:Envelope xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <TestResponse xmlns="https://example.org/sample"> <widget> <Name>MyWidget</Name> <NextWidget> <Name>MyWidget</Name> <NextWidget> <Name>MyWidget</Name> <NextWidget xsi:nil="true" /> </NextWidget> </NextWidget> </widget> </TestResponse> </soap:Body>

When the client-side proxy class deserializes this message, it instantiates three new Widget objects and sets up the list relationship. It then assigns the first Widget object to the w1 variable (originally passed by reference), but nothing changes with w2 or w3 since they weren't passed by reference. The thing to realize here is that w1 references a new Widget object now—this is not the same one that was passed in originally. However, after the call returns, the w2 and w3 variables still reference the original (empty) Widget objects. There are now two new Widgets hanging off of w1.

To get the results you want, you need to modify the client code to access the new objects attached to w1, as shown here:

ws.Test(ref w1); Console.WriteLine (w1.Name); Console.WriteLine (w1.NextWidget.Name); Console.WriteLine (w1.NextWidget.NextWidget.Name);

This will give you access to the new objects deserialized from the response message with the modified Name values.

Q When I call the following WebMethod supplying the same Widget object for w1 and w2, it always returns false. Why?

[WebMethod] public bool TestWidgets (ref Widget w1, ref Widget w2) { return (w1 == w2); // always returns false! }

Q When I call the following WebMethod supplying the same Widget object for w1 and w2, it always returns false. Why?

[WebMethod] public bool TestWidgets (ref Widget w1, ref Widget w2) { return (w1 == w2); // always returns false! }

A WebMethods use the document/literal style for SOAP messages by default. Literal means that a literal schema definition specifies exactly what goes in the SOAP body. The problem with using document/literal in this particular example is that XML Schema does not provide a built-in mechanism for maintaining object identity. Consider the following client code:

WidgetService ws = new WidgetService(); Widget w = new Widget("MyWidget"); bool same = Ws.TestWidgets(ref w, ref w);

A WebMethods use the document/literal style for SOAP messages by default. Literal means that a literal schema definition specifies exactly what goes in the SOAP body. The problem with using document/literal in this particular example is that XML Schema does not provide a built-in mechanism for maintaining object identity. Consider the following client code:

WidgetService ws = new WidgetService(); Widget w = new Widget("MyWidget"); bool same = Ws.TestWidgets(ref w, ref w);

When I supply the same Widget object to the proxy class, it gets serialized into the document twice (see Figure 5). Then, during deserialization (at the WebMethod) w1 and w2 become two separate Widget objects because there's no way to tell that they were originally the same object. You could define a mechanism on top of XML Schema for managing object identity, but there's nothing currently built into the WebMethod framework.

Figure 5 MyWidget Serialized Twice

<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <TestWidgets xmlns="https://example.org/sample"> <w1> <Name>MyWidget</Name> </w1> <w2> <Name>MyWidget</Name> </w2> </TestWidgets2> </soap:Body> </soap:Envelope>

The SOAP 1.1 specification defined a set of encoding rules for dealing with this type of situation (section 5 of the specification at https://www.w3c.org). These rules make it possible to identify an object uniquely through a combination of id and href attributes. Using this technique will enable the WebMethod to behave appropriately (although this approach has a problem that I will discuss shortly).

You can use rpc/encoded for a particular WebMethod by annotating it with the [SoapRpcMethod] attribute:

[WebMethod] [SoapRpcMethod] public bool TestWidgets (ref Widget w1, ref Widget w2) { return (w1 == w2); // behaves correctly! }

Now when you pass the same Widget object to the proxy class, it will be serialized using the SOAP encoding rules, which maintains object identity (see Figure 6). Notice that the w1 and w2 elements both refer to the same Widget element with an id of "#id1". This makes it possible for the WebMethod framework to maintain object identity after deserialization.

Figure 6 Maintaining Object Identity

<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="https://example.org/sample" xmlns:types="https://example.org/sample/encodedTypes" > <soap:Body soap:encodingStyle= "https://schemas.xmlsoap.org/soap/encoding/"> <tns:TestWidgets> <w1 href="#id1" /> <w2 href="#id1" /> </tns:TestWidgets> <types:Widget id="id1" xsi:type="types:Widget"> <Name>MyWidget</Name> </types:Widget> </soap:Body> </soap:Envelope>

Although the SOAP encoding rules solve this particular problem, they have also been the cause of many interoperability headaches over the years. As a result, the WS-I officially banned the use of SOAP-encoded messages in the basic profile. In general, you should avoid using the SOAP encoding rules and opt for using document/literal in order to maximize interoperability.

Q Is it possible to pass an endpoint reference to another Web service in a SOAP message?

Q Is it possible to pass an endpoint reference to another Web service in a SOAP message?

A Yes, this is different from passing objects by reference as discussed in some of the previous questions. Passing a reference to a Web service is about passing enough information in the message to allow the receiver to use a particular endpoint. The most basic piece of information required to invoke an endpoint is its location, which can be identified by a simple URI.

A Yes, this is different from passing objects by reference as discussed in some of the previous questions. Passing a reference to a Web service is about passing enough information in the message to allow the receiver to use a particular endpoint. The most basic piece of information required to invoke an endpoint is its location, which can be identified by a simple URI.

You can pass a URI like you would pass any other information in a SOAP message. The receiver can pick up the URI and, assuming it already knows how to interact with the service ahead of time, begin using the identified endpoint. A true reference, however, would provide additional metadata which is needed to figure out how to interact with the given endpoint on the receiving end.

The WS-Addressing specification proposes a standard technique for representing Web service endpoint references. WS-Addressing defines a new XML Schema type called wsa:EndpointReferenceType and it also provides an element named wsa:EndpointReference of type wsa:EndpointReferenceType. The following document illustrates the basic structure of wsa:EndpointReference:

<wsa:EndpointReference> <wsa:Address>xs:anyURI</wsa:Address> <wsa:ReferenceProperties> ... </wsa:ReferenceProperties> ? <wsa:PortType>xs:QName</wsa:PortType> ? <wsa:ServiceName PortName="xs:NCName"?>xs:QName</wsa:ServiceName> ? <wsp:Policy> ... </wsp:Policy>* </wsa:EndpointReference>

As you can see, this element allows you to specify the endpoint's address along with other information such as the port type, the service name, endpoint policies, and other custom properties.

WS-Addressing also defines several standard message information headers that are of type wsa:EndpointReferenceType (such as wsa:ReplyTo, wsa:FaultTo, wsa:From, and so forth). These headers will make it possible to build interesting message exchange patterns and systems that dynamically figure out which services to use at run time.

The .NET 1.1 Web services framework does not currently support WS-Addressing, but the new Web Services Enhancements (WSE) 2.0 technology preview does. With WSE 2.0 you can easily pass around endpoint references through an easy-to-use API.

Check out "Moving from WS-Routing to WS-Addressing" for more information on the latest WS-Addressing specification.

Q The SOAP response message generated by my WebMethod always includes an element with the same name as the method. This element then contains the result element. Is it possible to remove this wrapper element?

Q The SOAP response message generated by my WebMethod always includes an element with the same name as the method. This element then contains the result element. Is it possible to remove this wrapper element?

A Yes, you can control most of the details of the underlying SOAP message by applying additional attributes to your WebMethod signature. The attribute you want to use in this case is [SoapDocumentMethod]. You need to set the ParameterStyle to Bare instead of Wrapped (the default), as illustrated here:

[WebMethod] [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Bare)] public double Add(double x, double y) { return x + y; }

With this in place, a SOAP request message for this method will now look like this:

<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <x xmlns="https://example.org/sample">3</x> <y xmlns="https://example.org/sample">4</y> </soap:Body> </soap:Envelope>

The response SOAP message will look like this:

<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <AddResult xmlns="https://example.org/sample">7</AddResult> </soap:Body> </soap:Envelope>

A Yes, you can control most of the details of the underlying SOAP message by applying additional attributes to your WebMethod signature. The attribute you want to use in this case is [SoapDocumentMethod]. You need to set the ParameterStyle to Bare instead of Wrapped (the default), as illustrated here:

[WebMethod] [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Bare)] public double Add(double x, double y) { return x + y; }

With this in place, a SOAP request message for this method will now look like this:

<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <x xmlns="https://example.org/sample">3</x> <y xmlns="https://example.org/sample">4</y> </soap:Body> </soap:Envelope>

The response SOAP message will look like this:

<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <AddResult xmlns="https://example.org/sample">7</AddResult> </soap:Body> </soap:Envelope>

Q I've developed a class hierarchy having a base class named Employee. If I define a WebMethod that takes an Employee as input, is it possible to supply Employee-derived objects at run time?

Q I've developed a class hierarchy having a base class named Employee. If I define a WebMethod that takes an Employee as input, is it possible to supply Employee-derived objects at run time?

A You can substitute derived types to a WebMethod as long as you've annotated the base class with the [XmlInclude] attribute. You use the attribute to specify a derived type that's allowed for substitution at run time. The XmlInclude class hierarchy in Figure 7 illustrates how this works.

A You can substitute derived types to a WebMethod as long as you've annotated the base class with the [XmlInclude] attribute. You use the attribute to specify a derived type that's allowed for substitution at run time. The XmlInclude class hierarchy in Figure 7 illustrates how this works.

Figure 7 XmlInclude

[XmlInclude(typeof(Manager))] [XmlInclude(typeof(Programmer))] public class Employee { ... } public class Manager : Employee { ... } public class Programmer : Employee { ... }

This example indicates that it's possible to substitute Manager and Programmer instances wherever an Employee is expected. When this class hierarchy is mapped to XML Schema, the derivation remains intact through xsd:complexType derivation.

Now it's possible to supply Manager or Programmer instances to the WebMethod in Figure 8. Within the WebMethod you can check to see what type was actually supplied and process the object accordingly. However, the element found in the SOAP message must contain enough information for the WebMethod framework to determine the actual type. This is typically accomplished through an xsi:type annotation.

Figure 8 ProcessEmployee

[WebMethod] public void ProcessEmployee(Employee e) { if (e is Manager) { Manager m = e as Manager; // process as Manager here } else if (e is Programmer) { Programmer p = e as Programmer; // process as Programmer here } // process as Employee here }

In this case, a .NET-generated proxy class will still show the method as taking an Employee, but you can supply Employee-derived objects in the traditional object-oriented way:

EmployeeService s = new EmployeeService(); Programmer p = new Programmer(); s.ProcessEmployee(p);

When the Programmer object is serialized into the SOAP message, it's annotated with xsi:type="Programmer". This is how the WebMethod framework knows to deserialize a Programmer object on the receiving end.

Q How do I know if a particular field was specified in the XML document or given a default value during deserialization?

Q How do I know if a particular field was specified in the XML document or given a default value during deserialization?

A One way to tell is to enable XML Schema validation and specify in the schema that the elements are required. Check out the MSDN® Magazine article, "Extend the ASP.NET WebMethod Framework by Adding XML Schema Validation" for details on this technique.

A One way to tell is to enable XML Schema validation and specify in the schema that the elements are required. Check out the MSDN® Magazine article, "Extend the ASP.NET WebMethod Framework by Adding XML Schema Validation" for details on this technique.

A simpler approach, however, is to use an undocumented feature of the serialization engine that allows you to figure out if a given field was specified in the XML message. Here's how it works:

[WebMethod] public double Add(double x, double y, [XmlIgnore]bool xSpecified, [XmlIgnore]bool ySpecified) { if (!xSpecified || !ySpecified) throw new Exception( "you must provide both x and y values"); return x+y; }

You add a new Boolean parameter for every existing parameter with "Specified" appended to the end of the name. Then, if the particular field is found during deserialization, the "Specified" field is set to true; otherwise, it's set to false. This technique can also be used to determine if fields of a class were specified or defaulted during deserialization.

Send your questions and comments for Aaron to  xmlfiles@microsoft.com.

Aaron Skonnard teaches at Northface University in Salt Lake City. Aaron coauthored Essential XML Quick Reference (Addison-Wesley, 2001) and Essential XML (Addison-Wesley, 2000), and frequently speaks at conferences.