Chapter 5: XML Web Services (RPC the Easy Way)
XML Web services are, depending on your point of view, a breath of fresh air after the complexities of Microsoft .NET Remoting or a simplified approach to distributed code execution that lacks a few key features. Both arguments contain at least a grain of truth. More accurately, XML Web services represent a new approach to distributed architecture, one that favors simplicity, best design practices, and cross-platform use over raw communication speed. XML Web services provide a simple, robust, and flexible way to make a remote procedure call (RPC).
Most introductory .NET books provide a brief introduction to XML Web services and describe some of the underlying standards such as WSDL and SOAP. This chapter takes a more practical approach. We start with a quick introduction to XML Web services, examine how the technology differs from .NET Remoting, and consider where XML Web services fit into the design of an enterprise application. We also examine how you can make use of ASP.NET platform services in an XML Web service, publish the availability of an XML Web service, and consume XML Web services from other platforms.
|
|
Sample Chapter from the book:

Microsoft® .NET Distributed Applications: Integrating XML Web Services and .NET Remoting
|
The Role of XML Web Services in a Distributed SystemXML Web services play much the same role in an application as a component exposed through .NET Remoting. If you’re new to XML Web services, you might first think of something like a Web site that enables you to look up the shipping date for a package or an investment site that enables you to get real-time price quotes. However, both of these examples are really entire Web applications. They might rely on a back-end XML Web service to actually retrieve the information, but they also include a user interface in the form of a Web page.
XML Web services, like any other type of middle-tier component, do not provide any type of user interface. They are used exclusively by other applications and are never invoked directly by end users. For example, you might create a package or a stock quote lookup XML Web service and then call this XML Web service from an ASP.NET Web application or a desktop Windows client.
.NET XML Web services are often identified as ASP.NET Web services because they represent an extension of the ASP.NET platform. In ASP.NET, every Web page acts like an individual application that runs briefly on the Web server when requested and returns an XML result over HTTP. ASP.NET is optimized for this task and uses multiple worker threads, automatic process recycling, output caching, and other enterprise-level features to ensure it can handle a large volume of clients. XML Web services work in much the same way as Web pages: They run briefly on the Web server and return a result in an XML markup (generally as a SOAP message). Much as ASP.NET Web pages create the illusion of a continuously running Web application, XML Web services can create the illusion of a stateful object. In reality, however, XML Web services are stateless components that are created and executed just-in-time when a client invokes an XML Web service method. This is one of the most dramatic departures from .NET Remoting, which provides the flexibility to allow singleton and client-activated objects that remain in memory under a set lease lifetime policy.
.NET Remoting vs. XML Web ServicesXML Web services offer a simpler model for distributed applications than that provided by .NET Remoting. XML Web services are fine-tuned for Internet scenarios and cross-platform use. Although you can apply .NET Remoting in these scenarios, thanks to its SOAP formatter and HTTP channel, .NET Remoting is more suitable as a high-speed solution for binary communication between proprietary .NET components, usually over an internal network. And although XML Web services can’t match the communication speed of .NET Remoting over a binary channel, they still lend themselves to high-performance designs thanks to their stateless nature and ASP.NET’s multithreaded hosting service.
Here’s a quick overview of the major differences between the XML Web service and .NET Remoting technologies:
- XML Web services are more restricted than objects exposed over .NET Remoting. An XML Web service works in a similar way to a SingleCall .NET Remoting object. It isn’t possible to create a singleton or a client-activated object.
- Because of the restricted nature of XML Web services, the design issues are simplified. XML Web services are generally easier to create than remotable components (and easier to design well).
- Communication with .NET Remoting can be faster than XML Web service communication if you use a binary formatter. XML Web services support only SOAP message formatting, which uses larger XML text messages.
- XML Web services support open standards that target cross-platform use. For example, each .NET XML Web service has an associated WSDL document that describes how a client can interact with the service. Therefore, any client that can parse an XML message and connect over an HTTP channel can use an XML Web service, even if the client is written in Java and hosted on a UNIX computer.
- XML Web services are designed for use between companies and organizations. They can use a dynamic discovery mechanism or a UDDI registry that "advertises" services to interested parties over the Internet.
- XML Web services don’t require a dedicated hosting program because they are always hosted by ASP.NET. That also means that they gain access to some impressive platform services, including data caching, Web farm–ready session state management, authentication, and an application collection for global, shareable objects. These features, if required, can be extremely difficult to re-create by hand in a component exposed through .NET Remoting.
- Because XML Web services work through Microsoft Internet Information Services (IIS) and ASP.NET, they can communicate with clients using the default HTTP channel (usually port 80). This means that consumers can use XML Web services just as easily as they can download HTML pages from the Internet. There’s no need for an administrator to open additional ports on a firewall.
XML Web Service PlumbingXML Web services are based on a cluster of open standards. Table 5-1 lists the key players.
Table 5-1 XML Web Service Technologies
Standard |
Description |
WSDL (Web Services Description Language) |
An XML-based format that describes everything a client needs to interact with an XML Web service. This includes the XML Web service’s methods, the data types used for all parameters and return value, and the supported methods of communication. |
HTTP (Hypertext Transfer Protocol) |
The communication protocol used to send XML Web service requests and responses over the Internet. |
SOAP (Simple Object Access Protocol) |
An XML-based format used to encode the information in XML Web service messages. SOAP includes a rich, cross-platform way of representing common data types (such as integers, strings, and arrays). |
DISCO (Discovery) |
DISCO is Microsoft’s first crack at a lightweight way to create a simple list of XML Web service links. This can allow clients to discover the XML Web services offered by a particular organization. DISCO is slated for replacement by another similar standard called WS-Inspection, which was developed jointly with IBM. |
UDDI (Universal Description, Discovery, and Integration) |
The youngest and least evolved of the standards. UDDI is a business repository of XML Web service links designed to allow one business to find another based on the type of service, the business name, and so on. This specification is sure to go through many iterations before it becomes widely accepted. |
So how do all these standards fit together? We’ll begin with the process of creating the XML Web service, which can be summarized in three steps:
- Create a dedicated virtual directory to host the XML Web service on your Web server, using IIS.
- Code the custom XML Web service class, using the <WebMethod> attribute to mark each method that should be remotely callable. At its simplest, the XML Web service class is a collection of stateless methods. .NET takes care of the communication infrastructure that allows the client to discover and call these methods.
- Deploy the XML Web service files to the virtual directory.
The second step is for the client to find and use your XML Web service:
- The client finds the XML Web service, either through a predetermined URL or by using a discovery document or the UDDI registry.
- The client requests the WSDL document that describes the XML Web service. .NET creates this document quickly and automatically by examining your XML Web service.
- The client generates a proxy class based on the WSDL document. If the client is written using .NET, this step is performed automatically.
- The client uses the proxy class much as it would use an XML Web service class if it were instantiated in the local process. Behind the scenes, the proxy sends a SOAP message to the XML Web service and receives a SOAP message result. The proxy class handles the Internet communication and SOAP formatting automatically (as shown in Figure 5-1).

Figure 5-1 XML Web service interaction
This process is similar to .NET Remoting in several respects. First of all, the client communicates using a proxy class. The proxy class mimics the XML Web service and handles all the low-level details. Many programming platforms, including .NET, provide tools that create this proxy automatically.
The key difference is in how the proxy class is created. With .NET Remoting, the proxy object is generated dynamically. As soon as a .NET client instantiates a remote object, the runtime springs into action, creating the proxy based on the information in the assembly that describes the remote class. With XML Web services, the proxy class is created using the information found in the WSDL document. This is similar to the type of information contained in the assembly metadata, but it’s stored in a cross-platform, human-readable XML format.
Another difference is the fact that the XML Web service proxy class is created during the development cycle. If the XML Web service changes, the proxy class must be manually regenerated. This process sidesteps the headaches required with .NET Remoting, where the client always needs a reference to the assembly that describes the remote component (or a supported interface). The WSDL document also contains enough information for non-.NET clients to communicate with the XML Web service on their own, without requiring a proprietary tool. We’ll look at this issue at the end of the chapter.
The Role of IISWith XML Web services, you don’t worry about creating a server program that listens for requests and creates the required object. Instead, ASP.NET and IIS work together to perform this service for you.
IIS is the software that allows your computer to become a Web server and allows remote clients to download HTML pages (or run ASP.NET pages). IIS is included with Microsoft Windows 2000, Windows XP, and Windows .NET Server, but it isn’t necessarily installed by default. To ensure that your computer has IIS installed, try typing the following into your Web browser: http://localhost/localstart.asp.
Here, localhost is the special "loopback" alias that always refers to the current computer. (Alternatively, you can use the specific server name or IP address.) localstart.asp is a traditional ASP file that is stored in the root directory of your computer’s Web site home directory. If you receive an error when you attempt this request, you should check to make sure you have IIS installed.
To install IIS, follow these steps:
- From the Start menu, choose Settings, Control Panel.
- Select Add Or Remove Programs, and then click Add/Remove Windows Components.
- Select Internet Information Services in the list, and click Next to install the appropriate files.
You must install IIS on your development computer even if the XML Web service will ultimately be hosted on a different Web server.
For those who aren’t familiar with IIS, here are the key fundamentals you need to understand before you can create XML Web services:
- IIS provides access to the Web server through virtual directories. If you want to provide access to the C:\MyWeb directory, for example, you might create a virtual directory http://[ComputerName]/MyWeb. In this example, the virtual directory name (MyWeb) is the same as the physical directory name, but this isn’t required. If your computer is accessible over the Internet (or an intranet), clients can browse to a Web page in the virtual directory. For example, you can view the HTML page C:\MyWeb\index.html by requesting http://[ComputerName]/MyWeb/index.html.
- Virtual directories don’t have the same permissions as normal directories. By default, remote clients aren’t allowed to browse the virtual directory, run executables, create files, or download restricted file types (such as Microsoft Visual Basic source code files). You can configure some of these settings using IIS Manager and add further restrictions such as authentication requirements.
- IIS handles your computer’s Internet exposure. However, it doesn’t run ASP or ASP.NET pages or XML Web services. Instead, IIS maintains a list of registered file extensions. For example, ASP.NET pages (ASPX files) and XML Web services (.asmx files) are registered to the ASP.NET worker process. If IIS receives a request for one of these file types, it hands the work off to the ASP.NET worker process, which handles the rest of the work.
Creating a Virtual DirectoryBefore you create an XML Web service project in Visual Studio .NET, you should create a dedicated virtual directory. Otherwise, all your code files will be automatically located in a subdirectory of C:\Inetpub\wwwroot, which is the default home directory for IIS. This can cause a great deal of confusion, particularly when it comes time to move your XML Web service project to another computer.
Luckily, creating your own virtual directory is easy. Follow these steps:
- Create the physical directory on your hard drive.
- Start IIS Manager by choosing Settings, Control Panel, Administrative Tools, Internet Services Manager from the taskbar.
- Start the Virtual Directory Wizard in IIS Manager by right-clicking on the Default Web Site item (under your computer in the tree) and choosing New, Virtual Directory from the shortcut menu.
- Click Next to get started. Specify the alias, which is the name that your virtual directory will have for Web requests. Click Next to continue.
- Specify the physical directory that will be exposed as the virtual directory. Click Next to continue.
- The final wizard window gives you the chance to adjust the virtual directory permissions. The default settings allow clients to run ASP.NET pages and XML Web services but not make any modifications or upload files. This is the recommended configuration.
- Click Next, and then click Finish to end the wizard. You will see the virtual directory in the IIS Manager tree display (as shown in Figure 5-2). After you have created a virtual directory, you can create a Web project in it using Visual Studio .NET.

Figure 5-2 IIS Manager
The XML Web Service File FormatAs mentioned earlier, XML Web services are special files with the extension .asmx. The ASP.NET worker process is registered to handle Web requests for this type of file. When it receives a request, it loads the .asmx file, compiles the appropriate class, executes the relevant Web method, and returns the result. This process is similar to the process used for ASP.NET pages (.aspx files). However, .aspx files contain significant information about the controls used on a Web page. The .asmx file is really just a text document that tells ASP.NET where it can find the relevant XML Web service class. Technically, you can create an .asmx file that contains the code itself, but it’s almost always easier (and better in terms of organization) to place the XML Web service class in a separate file or dedicated assembly. This is also the model that Visual Studio .NET enforces.
The following sample .asmx file indicates that the XML Web service class called MyWebService can be found in a separate MyWebService.vb code file. ASP.NET is gracious enough to compile this file automatically when needed (a service that ordinary .NET components don’t provide).
<%@ WebService Language="VB" Codebehind="MyWebService.vb" Class="MyWebService" %>
And here’s a more typical example that indicates that the XML Web service class is named MyWebService and is already compiled into a DLL assembly called MyAssembly:
<%@ WebService Language="VB" Class="MyWebService,MyAssembly" %>
Most developers will program XML Web services with Visual Studio .NET. In this case, the .asmx file is created automatically. (In fact, Visual Studio .NET doesn’t even display the contents of .asmx files in the integrated development environment [IDE], but if you use Notepad to examine these files, you’ll see content similar to what’s shown here.) Visual Studio .NET also compiles all the XML Web service classes in the project into a single DLL assembly. Therefore, when it comes time to deploy the completed project to a production Web server, all you need to do is copy the individual .asmx files and a single DLL assembly. You can safely leave the Visual Basic source code files behind.
Creating an XML Web ServiceActually creating an XML Web service class is easy, which is one of the reasons XML Web services have gained far more exposure than .NET Remoting. All you need to do is import the System.Web.Services namespace and add a <WebMethod> attribute to each method that you want to expose in the XML Web service.
<WebMethod()> _ Public Sub AddCustomer(ByVal customer As CustomerDetails) ’ (Code omitted.) End Sub
Optionally, you can set additional <WebMethod> properties to configure a text description for each Web method, which can help the client understand how your service works. You can also add a <WebService> attribute to your class declaration (although this isn’t strictly required) to set a description for the entire service and associate an XML namespace with your service.
Listing 5-1 shows an example of the CustomerDB service provider component redesigned as an XML Web service. The differences are highlighted.
Imports System.Web.Services Imports System.Data.SqlClient Imports System.Data ‘ The XML namespace is not related to a .NET namespace. ‘ It simply associates a unique identifier that can be used ‘ to describe your service in the WSDL document. ‘ Typically, you will use a URL that you control. <WebService(Namespace:="http://www.prosetech.com/", _ Description:="Methods to interact with the Customers table.")> _ Public Class CustomerDB Private ConnectionString As String = _ "Data Source=localhost;Initial Catalog=Store;" & _ "Integrated Security=SSPI" <WebMethod(Description:="Add a new customer record.")> _ Public Sub AddCustomer(ByVal customer As CustomerDetails) Dim Sql As String = "INSERT INTO Customers " Sql &= "(FullName, EmailAddress, Password) VALUES (‘" Sql &= customer.Name & "‘, ’" Sql &= customer.Email & "‘, ’" Sql &= customer.Password & "‘)" ExecuteNonQuery(Sql) End Sub <WebMethod(Description:="Modify an existing customer record.")> _ Public Sub UpdateCustomer(ByVal customer As CustomerDetails) Dim Sql As String = "INSERT INTO Customers " Sql &= "(FullName, EmailAddress, Password) VALUES (‘" Sql &= customer.Name & "‘, ’" Sql &= customer.Email & "‘, ’" Sql &= customer.Password & "‘)" ExecuteNonQuery(Sql) End Sub <WebMethod(Description:="Modify an existing customer record.")> _ Public Sub UpdateCustomer(ByVal customer As CustomerDetails) Dim Sql As String = "UPDATE Customers SET " Sql &= "FullName=‘" & customer.Name Sql &= "‘, EmailAddress=‘" & customer.Email Sql &= "‘, Password=‘" & customer.Password Sql &= "‘ WHERE CustomerID=" & customer.ID.ToString() ExecuteNonQuery(Sql) End Sub <WebMethod(Description:="Delete an existing customer record.")> _ Public Sub DeleteCustomer(ByVal customerID As Integer) Dim Sql As String = "DELETE FROM Customers WHERE CustomerID=" Sql &= customerID.ToString() ExecuteNonQuery(Sql) End Sub ’ This private method is not remotely callable. Private Sub ExecuteNonQuery(ByVal sql As String) Dim con As New SqlConnection(ConnectionString) Dim cmd As New SqlCommand(sql, con) Try con.Open() cmd.ExecuteNonQuery() Catch Err As Exception Throw New ApplicationException( _ "Exception encountered when executing command.", Err) Finally con.Close() End Try End Sub <WebMethod(Description:="Retrieve a single customer record.")> _ Public Function GetCustomer(ByVal customerID As Integer) _ As CustomerDetails Dim Sql As String = "SELECT * FROM Customers Where CustomerID=" Sql &= customerID.ToString() Dim con As New SqlConnection(ConnectionString) Dim cmd As New SqlCommand(Sql, con) Dim reader As SqlDataReader Dim Customer As New CustomerDetails() Try con.Open() reader = cmd.ExecuteReader(CommandBehavior.SingleRow) reader.Read() Customer.ID = reader("CustomerID") Customer.Name = reader("FullName") Customer.Email = reader("EmailAddress") Customer.Password = reader("Password") Catch Err As Exception Throw New ApplicationException( _ "Exception encountered when executing command.", Err) Finally con.Close() End Try Return Customer End Function <WebMethod(Description:="Retrieve all customers in an array.")> _ Public Function GetCustomers() As CustomerDetails() Dim Sql As String = "SELECT * FROM Customers" Dim con As New SqlConnection(ConnectionString) Dim cmd As New SqlCommand(Sql, con) Dim reader As SqlDataReader Dim Customers As New ArrayList() Try con.Open() reader = cmd.ExecuteReader() Do While reader.Read() Dim Customer As New CustomerDetails() Customer.ID = reader("CustomerID") Customer.Name = reader("FullName") Customer.Email = reader("EmailAddress") Customer.Password = reader("Password") Customers.Add(Customer) Loop Catch Err As Exception Throw New ApplicationException( _ "Exception encountered when executing command.", Err) Finally con.Close() End Try ’ Now we convert the ArrayList to a strongly-typed array, ’ which is easier fo the client to deal with. ’ The ArrayList makes this possible through a handy ’ ToArray() method. Return CType(Customers.ToArray(GetType(CustomerDetails)), _ CustomerDetails()) End Function <WebMethod(Description:="Retrieve all customers in a DataSet.")> _ Public Function GetCustomersDS() As DataSet Dim Sql As String = "SELECT * FROM Customers" Dim con As New SqlConnection(ConnectionString) Dim cmd As New SqlCommand(Sql, con) Dim Adapter As New SqlDataAdapter(cmd) Dim ds As New DataSet Try con.Open() Adapter.Fill(ds, "Customers") Catch Err As Exception ’ Use caller inform pattern. Throw New ApplicationException( _ "Exception encountered when executing command.", Err) Finally con.Close() End Try Return ds End Function End Class
Public Class CustomerDetails Public ID As Integer Public Name As String Public Email As String Public Password As String End Class
Listing 5-1 The CustomerDB service provider as an XML Web service
Remember, if you aren’t designing your class with Visual Studio .NET, you also need to create the .asmx file that "advertises" the XML Web service to ASP.NET and move both files to a virtual directory. Visual Studio .NET performs these minor steps automatically.
Data SerializationOne difference you might have noticed in the XML Web service example is the downgraded CustomerDetails class. In the .NET Remoting example, the CustomerDetails class included full property procedures, several constructors, and a <Serializable> attribute. In an XML Web service, none of these is necessary. In fact, you could create property procedures and constructors, but the client wouldn’t be able to use them. Instead, the client would receive a stripped-down version of the class that only uses public member variables (like the version shown in the preceding example).
Why the discrepancy? Even though XML Web services and .NET Remoting components can both send SOAP messages, they use different formatters. Components exposed through .NET Remoting or saved to disk require the <Serializable> attribute and use the SoapFormatter class from the System.Runtime.Serialization.Formatters.Soap namespace. Web services, on the other hand, make use of the XmlSerializer class in the System.Xml.Serialization namespace. Both of these classes transform .NET objects into XML messages. The difference is that the SoapFormatter can exactly reproduce any serializable .NET object, provided it has the assembly metadata for the class. This involves some proprietary logic. The XmlSerializer, in contrast, is designed to support third-party clients who might understand little about .NET classes and assemblies. It uses predetermined rules to convert common data types. These rules, which are based on XSD and the SOAP standard, allow for the encoding of common data types, arrays, and custom structures but don’t provide any standard way to represent code (such as constructors and property procedures). Therefore, these details are ignored.
Table 5-2 lists the data types supported in XML Web services and the SOAP standard.
Table 5-2 Data Types Supported in XML Web Services
Data Type |
Description |
Basic data types |
Standard types such as integers and floating-point numbers, Boolean variables, dates and times, and strings are fully supported. |
Enumerations |
Enumerations types (defined by using the Enum keyword) are also fully supported. |
Custom objects |
You can pass any object you create based on a custom class or structure. The only limitation is that only data members can be transmitted. If you use a class with defined methods, the copy of the object that is transmitted will have only its properties and variables. |
DataSet objects |
DataSet objects are natively supported. They are returned as simple structures, which .NET clients can automatically convert to full DataSet objects. DataTable objects and DataRow objects, however, are not supported. |
XmlNode objects |
Objects based on System.Xml.XmlNode are representations of a portion of an XML document. Under the hood, all Web service data is passed as XML. This class enables you to directly support a portion of XML information whose structure might change. |
Arrays and collections |
You can use arrays and simple collections of any supported type, including DataSet objects, XmlNode objects, and custom objects. |
Testing the XML Web ServiceASP.NET provides a simple way to test an XML Web service after you have deployed it to a virtual directory. If you request the .asmx file in a Web browser, you’ll see an automatically generated test page that lists the available methods and the text descriptions you’ve added (as shown in Figure 5-3).

Figure 5-3 The CustomerDB test page
To test a Web method, you click one of the methods. A separate window will appear, with text boxes provided for parameters and with an Invoke button. When you click Invoke, the request message is sent, the XML Web service class is created, the Web method executes, the XML Web service class is destroyed, and the result is returned as an XML document that appears in the browser. Figure 5-4 shows the result of invoking the GetCustomers method from the test page. It returns an XML structure representing an array with five customers (two of which are collapsed in the screen shot).

Figure 5-4 The result of invoking GetCustomers
It’s important to understand that this technique does not require any special functionality from your Internet browserit’s simply a convenience provided by ASP.NET. If you were to type the exact same request string into a Netscape or Opera browser, you would receive the same test page and the same XML document. The request string is actually a request to the XML Web service using HTTP GET. It identifies the XML Web service, Web method, and any required parameters:
http://[Server]/[VirtualDirectory]/[AsmxFile]/[WebMethod]?[Arguments]
Here’s the HTTP GET request required to get the list of all customers:
http://localhost/CustomerDB/CustomerDB.asmx/GetCustomers?
And here’s another HTTP GET request string; this one retrieves information for the single customer record with the CustomerID of 1:
http://localhost/CustomerDB/CustomerDB.asmx/GetCustomer?customerID=1
Note that when you use the test page, the method of communication is HTTP GET (not SOAP). This means that you won’t be able to test some methods in your browser, such as UpdateCustomer.
The WSDL DocumentYou also can request the WSDL document for your XML Web service by requesting the .asmx file with ?WSDL appended to the end of the request string. (The test page links directly to this document; just click Service Description.) The WSDL document has the same role the IDL file does in COM programming, except it uses an XML-based syntax rather than a C-based syntax.
The WSDL document is far from compact. It’s designed to be independent of communication protocols (like HTTP) and encoding methods (like SOAP), which gives it the ability to be used with future technologies. Unfortunately, this also means that the WSDL document requires a lengthy, multipart structure. If you browse through the WSDL document for the CustomerDB XML Web service, you’ll find information about the supported methods and the custom-defined CustomerDetails class (excerpted here):
<s:complexType name="CustomerDetails"> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="ID" type="s:int" /> <s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="Email" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="Password" type="s:string" /> </s:sequence> </s:complexType>
Fortunately, there is little need to read the WSDL document directly. If you’re creating a .NET client, you can create a proxy class based on the WSDL document automatically. The same feat is possible with many third-party tools in non-.NET languages.
Consuming the XML Web Service.NET enables you to create an XML Web service proxy class in two ways. If you are using Visual Studio .NET, you can generate the proxy class using the Web reference feature (as shown in Figure 5-5). Just start a normal ASP.NET, Console, or Windows client project. Then right-click on the project in Solution Explorer and choose Add Web Reference. You can then enter the WSDL document location, or you can enter a Web URL that identifies a page or discovery document that links to the XML Web service and then browse to it using the appropriate link.

Figure 5-5 Adding a Visual Studio .NET Web reference
Figure 5-5 shows the version of the Add Web Reference dialog box that you’ll see in Visual Studio .NET 2003. In earlier versions, the window is functionally equivalent but looks a little different. Visual Studio .NET 2003 also adds a few links that allow you to easily search for UDDI Web servers and browse the information they provide.
When you click Add Reference, the proxy class is generated in the language of the current project and is added to the project. By default, however, the proxy class is hidden because it shouldn’t be modified directly. If you want to examine the proxy class, choose Project, Show All Files from the Visual Studio .NET menu. By default, the proxy class file has the name Reference.vb and is grouped under the server name in the Web References section of Solution Explorer (as shown in Figure 5-6).

Figure 5-6 The Reference.vb proxy class file
Even if you aren’t using Visual Studio .NET, you can still create the proxy class using the WSDL.exe command-line utility that is included with the .NET Framework. This utility is found in the .NET Framework directory, such as C:\Program Files\Microsoft.NET\FrameworkSDK\Bin.
The syntax for WSDL.exe is shown here. Table 5-3 describes each field.
wsdl /language:language /protocol:protocol /namespace:myNameSpace /out:filename /username:username /password:password /domain:domain <url or path>
A typical WSDL.exe command looks something like this (split over two lines):
wsdl /language:VB /namespace:localhost http://localhost/CustomerDB/CustomerDB.asmx?WSDL
This creates a CustomerDB.vb proxy class file. You can then add this file to a client project or compile to a DLL using the vbc.exe command-line compiler, as shown here:
vbc /t:library CustomerDB.vb
Table 5-3 WSDL.exe Command-Line Parameters
Parameter |
Description |
Language |
This is the language that the proxy class will use. The language really doesn’t matter because you won’t directly modify the proxy class code (because any changes are lost every time you regenerate it). The default is C#. |
Protocol |
Usually, you will omit this option and use the default (SOAP). However, you can also specify HTTP GET and HTTP POST for more limiting protocols. |
Namespace |
This is the .NET namespace that your proxy class will use. If you omit this parameter, no namespace is used and the classes in this file are available globally. For better organization, you should choose a logical namespace. Visual Studio .NET automatically uses the server name (for example, localhost). |
Out |
This enables you to specify the name for the generated file. By default, this is the name of the service followed by an extension indicating the language (for example, CustomerDB.vb). |
Username, password, and domain |
You should specify these values if the Web server requires authentication to access the virtual directory. |
URL or path |
This piece of information is always added at the end of the WSDL.exe command line. It specifies the location of the WSDL document for the Web service. |
Examining the Proxy ClassIt’s not necessary to examine the proxy class, but doing so provides some interesting insights into how the .NET XML Web service framework works. Essentially, the proxy class mimics the XML Web service, reproducing every Web service method. The client then creates an instance of the proxy class and calls one of its methods. Behind the scenes, the proxy class packages the information into a SOAP request message, waits for the SOAP response message, and then converts the return value into a standard .NET data type, which it then passes back to the client.
Listing 5-2 shows a shortened version of the autogenerated CustomerDB proxy class. Notice that it includes a copy of the CustomerDetails class, which allows the client to use this type natively. The proxy class also includes specialized Begin and End versions of each Web method (as in BeginGetCustomer and EndGetCustomer), which allow the Web method to be called asynchronously. These methods, which aren’t shown here, are examined in more detail in the next chapter.
Imports System.Web.Services Imports System.Web.Services.Protocols Imports System.Xml.Serialization Namespace localhost <WebServiceBinding(Name:="CustomerDBSoap", [Namespace]:="http://www.prosetech.com/"), _ XmlIncludeAttribute(GetType(System.Object()))> _ Public Class CustomerDB Inherits SoapHttpClientProtocol Public Sub New() MyBase.New Me.Url = _ "http://localhost/WebServices/CustomerDB/CustomerDB.asmx" End Sub <SoapDocumentMethod("AddCustomer"> _ Public Sub AddCustomer(ByVal customer As CustomerDetails) Me.Invoke("AddCustomer", New Object() {customer}) End Sub <SoapDocumentMethod("UpdateCustomer"> _ Public Sub UpdateCustomer(ByVal customer As CustomerDetails) Me.Invoke("UpdateCustomer", New Object() {customer}) End Sub <SoapDocumentMethod("DeleteCustomer"> _ Public Sub DeleteCustomer(ByVal customerID As Integer) Me.Invoke("DeleteCustomer", New Object() {customerID}) End Sub <SoapDocumentMethod("GetCustomer"> _ Public Function GetCustomer(ByVal customerID As Integer) _ As CustomerDetails Dim results() As Object = Me.Invoke("GetCustomer", _ New Object() {customerID}) Return CType(results(0),CustomerDetails) End Function <SoapDocumentMethod("http://www.prosetech.com/GetCustomers"> _ Public Function GetCustomers() As CustomerDetails() Dim results() As Object = Me.Invoke("GetCustomers", _ New Object(-1) {}) Return CType(results(0),CustomerDetails()) End Function <SoapDocumentMethod("http://www.prosetech.com/GetCustomersDS"> _ Public Function GetCustomersDS() As System.Data.DataSet Dim results() As Object = Me.Invoke("GetCustomersDS", _ New Object(-1) {}) Return CType(results(0),System.Data.DataSet) End Function End Class <System.Xml.Serialization.XmlTypeAttribute()> _ Public Class CustomerDetails Public ID As Integer Public Name As String Public Email As String Public Password As String End Class End Namespace
Listing 5-2 The proxy class for the CustomerDB service
The proxy class gains the ability to send SOAP messages over the Internet because it inherits from the SoapHttpClientProtocol class, which provides an Invoke method. Notice that each method receives a result as a generic object, which it then converts to the expected strongly typed class or structure.
By contrast, .NET Remoting uses its own dynamic proxy mechanism. .NET Remoting proxies are created at run time, whereas XML Web service proxies are created at design time. As a side effect, if the XML Web service changes, you must first recompile the XML Web service and then regenerate the proxy class. In Visual Studio .NET, you can accomplish this quickly by right-clicking on the Web reference in Solution Explorer and choosing Update Web Reference.
Using the Proxy ClassUsing the proxy class is easyin fact, it works just like any other object. Listing 5-3 and Figure 5-7 show a sample Windows client that gets the list of all customers and displays in a DataGrid control. This example is nearly identical to the local data component example in Chapter 3, even though the method call is invoked over the Internet. If you use Visual Studio .NET, you can even enter Break mode and single-step from the client code into the XML Web service code.
Public Class WindowsClient Inherits System.Windows.Forms.Form ’ (Designer code omitted.) Private Sub WindowsClient_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim DBProxy As New localhost.CustomerDB() DataGrid1.DataSource = DBProxy.GetCustomerDS().Tables(0) End Sub End Class
Listing 5-3 A basic Windows client

Figure 5-7 Retrieving query results through an XML Web service
And here’s an example that shows how you can interact with the CustomerDetails object defined by the XML Web service:
Public Class WindowsClient Inherits System.Windows.Forms.Form ’ (Designer code omitted.) Private Sub WindowsClient_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim DBProxy As New localhost.CustomerDB() Dim Customer As localhost.CustomerDetails Customer = DBProxy.GetCustomer(1) MessageBox.Show("CustomerID 1 is: " & Customer.Name) End Sub End Class
Keep in mind that this is really just a remote procedure call. The XML Web service doesn’t retain any state. Therefore, even though you retain a reference to the proxy class, the actual XML Web service instance is created at the beginning of every method invocation and is destroyed at the end. This means that you can’t set instance variables or properties and expect the values to persist between method calls.
By default, the proxy sends its SOAP message over the default HTTP port used for Web traffic: 80. You can also tweak some proxy class settings to adjust this communication if needed. If you need to connect through a computer called MyProxyServer using port 80, for example, you can use the following code before you call any proxy methods:
Dim ConnectionProxy As New WebProxy("MyProxyServer", 80) Dim DBProxy As New localhost.CustomerDB() DBProxy.Proxy = WebProxy
Similarly, you can adjust the amount of time the proxy waits for a SOAP response before timing out and throwing an exception, as follows:
DBProxy.Timeout = 10000 ’ 10000 milliseconds is 10 seconds.
Debugging Web ServicesIt’s possible to debug an XML Web service in a separate instance of Visual Studio .NET or as part of a multiproject solution. However, you’ll need to tweak the debugging settings so that your XML Web service code is compiled and loaded into the debugger. Otherwise, you won’t be able to set breakpoints, use variable watches, or rely on any of the other indispensable Visual Studio .NET debugging features.
To enable XML Web service debugging, right-click on the project in Solution Explorer and choose properties. Then under the Configuration Properties | Debugging node, choose Wait For External Process To Connect, as shown in Figure 5-8. This instructs Visual Studio .NET to load the debugging symbols when you run the XML Web service, instead of simply showing the XML Web service test page.

Figure 5-8 Configuring an XML Web service for debugging
If you’re testing a compiled client or a client in another instance of Visual Studio .NET, just be sure to start the XML Web service project first in order to debug it. If you’re creating a solution that includes both a client and an XML Web service, you can configure it to have multiple startup projects, exactly as you would with .NET Remoting. Simply right-click on the solution item in the Solution Explorer, and choose Properties. Figure 5-9 shows an example in which both an XML Web service and a client application are launched when the solution is started.
While testing an XML Web service, you might encounter the infamous "unable to start debugging on the server" error. This signals that Visual Studio .NET was able to compile the XML Web service but can’t execute it in debug mode. Unfortunately, this problem can result because of countless different reasons. One common problem occurs if you install IIS after you install the .NET Framework. In this case, you can "repair" the .NET Framework using the original setup CD or DVD. Microsoft describes this processand a number of other possible causes for this errorin a white paper at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtbshttpservererrors.asp. One common cause not mentioned is failing to create the virtual directory or trying to run a program after you’ve removed or modified the virtual directory. To correct these problems, modify the virtual directory settings in IIS Manager.

Figure 5-9 Starting an XML Web service and client
Another problem that can occur in Visual Studio .NET 2003 is a failure to authenticate. Visual Studio .NET 2003 attempts to access the local Web server using integrated Windows authentication, even if you have anonymous authentication enabled for the virtual directory. Thus, your virtual directory must allow both anonymous and Windows Integrated authentication. To allow for this, follow these steps:
- Start IIS Manager.
- Right-click the virtual directory for your application, and choose Properties. (Alternatively, you can configure authentication for all directories if you right-click the Web Sites folder and choose Properties.)
- Select the Directory Security tab.
- In the Anonymous access and authentication section, click the Edit button.
- In the Authentication Methods dialog box, under Authenticated access, select Integrated Windows Authentication, as shown in Figure 5-10.

Figure 5-10 Enabling Windows authentication for debugging
- Click OK to apply your changes.
ASP.NET Platform ServicesThe ASP.NET platform provides a rich set of services for all types of Web applications. You can harness many of these features in your XML Web services just by deriving your XML Web service class from System.Web.Service.WebService.
<WebService(Namespace:="http://www.prosetech.com/", _ Description:="Methods to interact with the Customers table.")> _ Public Class CustomerDB Inherits WebService
By inheriting from WebService, your class gains four additional properties, as shown in Table 5-4. These features include a few heavyweights, such as authentication and caching, that we’ll return to in later chapters. These prebuilt features are so useful that in many situations you’ll find it easier to build a large-scale performance-optimal solution with XML Web services rather than .NET Remoting, even though .NET Remoting supports leaner binary communication.
Table 5-4 WebService Properties
Property |
Description |
Application |
Provides a global collection where you can store any .NET object. |
Context |
Provides access to some ASP.NET intrinsic objects, such as Request and Response. Most of these aren’t useful in XML Web services because you are interacting with a proxy class, not directly with the client. However, one object (Cache) is extremely useful for storing frequently used information. You’ll learn how to master this object in Chapter 12. |
Server |
Provides server utility methods for tasks such as encoding HTML content and URL query strings. These features generally aren’t useful in an XML Web service. |
Session |
Provides a collection where you can store information that’s bound to the current request. However, this capability can easily waste server memory and is disabled by default. |
User |
Enables you to retrieve information about the client if it has logged on through the proxy class using some form of integrated IIS authentication. You’ll learn about this optionand some more flexible alternativesin Chapter 13. |
You could also access these properties without deriving from WebService, by using the System.Web.HttpContext class.
SOAP Headers and SOAP ExtensionsSOAP headers and extensions are great ways to extend the SOAP standard, and they add support for custom encryption, logging, compression, and authentication schemes. SOAP headers and extensions are also potential nightmares because they represent proprietary add-ons that you can apply to your XML Web services, potentially making them less generic and more difficult to support on other platforms. SOAP headers and extensions are also wedded to the SOAP standard, meaning you can’t easily adapt a SOAP header authentication technique or SOAP extension logging approach to a .NET Remoting component that works over a TCP/IP channel with binary messages.
This chapter won’t discuss SOAP headers and extensions. However, Chapter 13 and Chapter 18 show how you can use SOAP headers to implement security in conjunction with a custom ticket-based authentication system.
Publishing an XML Web ServiceBecause XML Web services aim to share functionality between organizations, there needs to be some mechanism that enables an interested party to discover which XML Web services another entity offers. The most ambitious part of this strategy is the UDDI registry, which is still in its infancy. Essentially, UDDI is a specification that defines how an XML Web service registry should be organized and how XML Web services can be registered programmatically. There are currently several test UDDI implementations, including the Microsoft Registry at http://uddi.microsoft.com/developer. You can find general information about the UDDI standard at http://www.uddi.org. A link in a UDDI registry can point to a WSDL document or to a discovery document listing XML Web services. This book does not cover UDDI in any detail.
Microsoft also supports a simpler discovery standard. In the current version of Visual Studio .NET, this is DISCO (which is slated for replacement by a similar standard called WS-Inspection, as mentioned earlier). A DISCO document is essentially an XML file with a list of links to one or more WSDL documents. A client using Visual Studio .NET can enter the URL for a discovery document when adding a Web reference and can easily find the required XML Web service. Essentially, a discovery document offers a simple approach to sharing XML Web service URLs in a standardized format.
A typical discovery document has the extension .disco or .vsdisco and contains one or more <contractRef> elements, each of which points to a WSDL document. It can also contain <discoveryRef> links that point to other discovery files or <schemaRef> links that point to XSD documents. Here’s an example that can allow a client to locate the CustomerDB XML Web service:
<?xml version="1.0" encoding="utf-8" ?> <disco:discovery xmlns:disco="http://schemas.xmlsoap.org/disco" xmlns:wsdl="http://schemas.xmlsoap.org/disco/wsdl"> <wsdl:contractRef ref="http://MyServer/WebServices/CustomerDB.asmx?WSDL"/> </disco:discovery>
Note that the example doesn’t use the localhost reference because the client requires the real name of the server.
Microsoft also supports a dynamic discovery format. When a client requests this file, the Web server automatically creates a list of all the XML Web services in the given virtual directory and any subdirectories. You can specifically exclude search paths (typically to save time) by using the <exclude> attribute, although this doesn’t stop the client from adding a reference to an XML Web service in one of these directories if it is not secured through IIS.
<?xml version="1.0" encoding="utf-8" ?> <dynamicDiscovery xmlns="urn:schemas-dynamicdiscovery:disco.2000-03-17"> <exclude path="_vti_cnf" /> <exclude path="_vti_pvt" /> <exclude path="_vti_log" /> <exclude path="_vti_script" /> <exclude path="_vti_txt" /> </dynamicDiscovery>
Cross-Platform XML Web ServicesYou’ve already seen how you can invoke an XML Web service in any browser using HTTP GET and the properly formatted query string. This gives you an idea of how easy it is for a non-.NET client to access an XML Web service. It just has to send the appropriate HTTP request and parse the retrieved XML document.
To test this technique, you can use the Microsoft XML COM library, which includes an XMLHTTP object that can send XML documents over the HTTP channel. This example (shown in Listing 5-4 and illustrated in Figure 5-11) uses Visual Basic 6 and Microsoft XML version 4 (msxml4.dll), but earlier versions will also work.
Private Sub Form_Load() Dim Transfer As XMLHTTP Set Transfer = New XMLHTTP ’ Call the XML Web Service using HTTP GET. Transfer.open "GET", _ "http://localhost/WebServices/CustomerDB/CustomerDB.asmx/" & _ GetCustomers", False Transfer.send ’ Retrieve the XML response. Dim Doc As DOMDocument Set Doc = Transfer.responseXML ’ Configure the MSFlexGrid control. MSFlexGrid1.TextMatrix(0, 0) = "ID" MSFlexGrid1.TextMatrix(0, 1) = "Name" MSFlexGrid1.TextMatrix(0, 2) = "Email" MSFlexGrid1.TextMatrix(0, 3) = "Password" ’ Parse the response. Dim Child As MSXML2.IXMLDOMNode For Each Child In Doc.documentElement.childNodes ’ The first node (offset 0) is the ID. ’ The second node (offset 1) is the name. ’ The third node (offset 2) is the email. ’ The fourth node (offset 3) is the password. MSFlexGrid1.AddItem (Child.childNodes(0).Text & vbTab & _ Child.childNodes(1).Text & vbTab & Child.childNodes(2).Text & _ vbTab & Child.childNodes(3).Text) Next End Sub
Listing 5-4 Consuming a .NET XML Web service in Visual Basic 6

Figure 5-11 A Visual Basic 6 XML Web service client
Ideally, the client will use a slightly more advanced technique and will use SOAP messages. You can send and receive SOAP messages using the XMLHTTP object, but the client will need to shoulder the burden of manually creating the request message and processing the response. If the XML Web service changes, the client must also adapt. However, most platforms include more sophisticated tools that enable you to send SOAP messages using a higher-level abstraction and even create proxy classes by analyzing WSDL documents. Some examples include the following:
- Microsoft’s SOAP Toolkit, which works with the previous generation of Visual Studio products (such as Visual Basic and C++)
- IBM’s Web Service Development Kit
- Oracle’s Web Integration Development Language
- Sun Microsystems’s Sun ONE (Open Net Environment) offering, which now supports Web services written in the Java language
- Perl’s SOAP::Lite kit, which provides basic SOAP functionality
SummaryThis chapter has provided a lightning-quick introduction to XML Web services. I’ve steered away from many conventional topics (such as an examination of the SOAP standard), which are discussed to excess in most introductory .NET texts. Instead, this chapter has honed in on some more useful XML Web service topics, including cross-platform development, serialization, and ASP.NET platform services. The coming chapters show how you can work with XML Web services in even more flexible ways, including using asynchronous requests and authentication. I haven’t introduced these topics yet because they represent techniques that can be applied with benefit to both XML Web services and .NET components exposed through .NET Remoting.
This chapter also touched on some topics that we won’t return to in any detail, including ASP.NET pages and IIS configuration. If you would like to learn more about the ASP.NET platform and how to configure a Web server, you might be interested in a dedicated ASP.NET book, such as my ASP.NET: The Complete Reference (Osborne McGraw-Hill, 2002). Of course, you’ll need to return to this book to learn many of the best practices to follow when you implement these features in an enterprise application.
Finally, if you would like to try interacting with a prebuilt XML Web service, the Internet provides many choices. You can try creating client applications for the following:
- Microsoft’s TerraService (http://terraservice.net), which provides a free gateway to TerraServer, the multiterabyte database of satellite photography. You can use TerraService to download satellite images and display them in your .NET applications.
- Microsoft’s MapPoint, which enables you to access high-quality maps and geographical information. MapPoint isn’t free, but you can use a free trial of the XML Web service. See http://msdn.microsoft.com/library/en-us/dnmapnet/html/mapintronet.asp for information.
The basic services are on GotDotNet (http://www.gotdotnet.com/playground/services); they include tried-and-true favorites such as the thumbnail generator, the mail sender, and the quote of the day. Best of all, you can also see the .NET implementation code for the XML Web services.
|