<summary> Spelunking Microsoft Technologies - Win32, .NET and Rotor </summary>
using WinToolZone;

Skip Navigation Links
Home
Latest
.NETExpand .NET
WindowsExpand Windows
Publications
PresentationsExpand Presentations
Spoken At...
My...Expand My...
GuestbookExpand Guestbook
Feedback
Search

You are visitor  counter


















Generating WebService proxies in .NET 

WebServices have become the de-facto mechanism for remote object method invocation across the Internet. Infact, so much is their impact that newer standards like WS-Security, WS-SecureConversation and more, have come up, to help them participate more in enterprise level applications.

From the perspective of a client consuming the webservice, may that client be a rich desktop application or a thin web application, an important component involved in the communication between the client and the webservice is the webservice proxy. A Webservice proxy is the entity that allows for the client to invoke the methods of the webservice, which might be residing anywhere across the Internet, as if the method invocation was local. And this proxy is created, in .NET Framework, using the WSDL utility. But what if you wanted to generate a proxy for a webservice programmatically? How would you do that?

In this article, we shall delve into using one of the FCL (.NET Framework Class Library) classes that is hidden deep inside the FCL forest and use it generate the proxy for a webservice programmatically. And not just that, we shall use CodeDOM to generate it in the language of our choice! So, this implies that you could create a webservice proxy in Managed C++! Ofcourse, you will need to have .NET Framework 1.1 for that to happen :)


Introducing ServiceDescriptionImporter

System.Web.Services.dll assembly contains a namespace, System.Web.Services.Description. This namespace has a good amount of classes inside it, including the one that will generate the webservice proxy for us, called ServiceDescriptionImporter.

Using ServiceDescriptionImporter to generate the proxy for a webservice involves the following steps:

  1. Specify the WSDL (Webservice Description Language) file for the webservice in question
  2. Create a CodeDOM representation and produce the webservice proxy as a CodeDOM representation
  3. Convert the CodeDOM representation to the language specific code

And thus you have your own webservice proxy. Let's go through each of the above three steps and generate a webservice proxy. The webservice that we shall use is very simple in nature: it shall simply add two numbers and return their sum. This is the source for the same:

<%@ WebService language="C#" class="CAdd" %>

using System;
using System.Web.Services;
using System.Xml.Serialization;

public class CAdd {

    [WebMethod]
    public int Add(int a, int b) {
        return a + b;
    }
}

Now, to view the WSDL for a .NET hosted webservice, the webservice needs to be invoked by suffixing 
?WSDL to its URI. On my system, the WSDL is made available via the URI http://localhost/wsproxygen/add.asmx?WSDL. WSProxyGen is the virtual folder under which the webservice has been hosted.

To read this WSDL, we use the WebClient class's DownloadData method as shown below:

WebClient wcWSDL = new WebClient();
byte[]arrWSDL = wcWSDL.DownloadData(
http://localhost/wsproxygen/add.asmx?wsdl);

// write WSDL to a file
FileStream fsWSDL = File.Create("webservice.wsdl");
fsWSDL.Write(arrWSDL,0,arrWSDL.Length);
fsWSDL.Close();

The WSDL is returned to us as a byte array, which we write to a file called webservice.wsdl. Now that we have the WSDL, its time to move to the next step and create a CodeDOM representation of this WSDL.


Generating CodeDOM based webservice proxy

To read, format and parse the WSDL that we have, and produce the webservice proxy from it, we need to read the webservice description it contains. And this is done using ServiceDescription class's Read method. This is (again) contained in the System.Web.Services.Description namespace, in the System.Web.Services.dll, and is done as shown below:

// create a service description

ServiceDescription sd = ServiceDescription.Read("webservice.wsdl");

The static Read method returns us a ServiceDescription object reference. Next, to do the actual import and produce the proxy, we create ServiceDescriptionImporter object and point it to the service description object from which to perform the import:

// instantiate an importer and load the service description in it..

ServiceDescriptionImporter imp = new ServiceDescriptionImporter();
imp.ProtocolName = "SOAP";
imp.ServiceDescriptions.Add(sd);

The ProtocolName property is used to specify the protocol in which the service description has been presented, and in this case, its the SOAP protocol. Finally, we add the service description we created to the ServiceDescriptions collection of the importer. The importer works on the service descriptions contained in its collection to produce the proxy for each of them.

The next step is to create the CodeDOM representation of the webservice proxy by creating the appropriate CodeDOM objects:

// create a codeDOM namespace, its code unit and perform the import against it...

CodeNamespace ns = new CodeNamespace("NSWinToolZone");
CodeCompileUnit ccu =
new
CodeCompileUnit();
ccu.Namespaces.Add(ns);
imp.Import(ns,ccu);

For those who are new to CodeDOM or are not aware of what it does, here's brief understanding of the same. CodeDOM is a way to represent your program logic in memory using generic in-memory constructs. For instance, an application's source code file is represented as a CodeCompileUnit. Each CodeCompileUnit has a collection to which namespaces can be added, which is grammatically equivalent to doing a using System or Imports System language specific ways to refer to the usage of a namespace. The advantage of using CodeDOM is that once the entire source code is represented by a CodeDOM structure, it can be converted to any language specific code by using the relevant CodeProvider. Support for CodeDOM is present in the System.CodeDOM namespace, in the System.dll assembly.

Now, the ServiceDescriptionImporter produces the code for the webservice proxy as a CodeDOM representation. For this, we create a new namespace under which the code shall be produced, the name of of which is set to NSWinToolZone.

CodeNamespace is the way to create a new namespace and we create it, and subsequently add it to the Namespaces collection of the CodeCompileUnit. Finally, we invoke the Import method of the ServiceDescriptionImporter object that uses the service descriptions specified in its ServiceDescriptions collection and create the CodeDOM representation of the webservice proxy code in the code compile unit object we pass in as the second argument to the method.

Now that we generated the WSDL, and created the webservice proxy in its CodeDOM format, let's proceed to the final step: producing language specific code from the CodeDOM representation that we have.


Generating compiler specific webservice proxy

To produce language specific code, we got to use entities that are termed as code-providers. These are set of classes that the .NET framework has built-in support for and when pointed to a CodeDOM based representation of a code, will parse it and spit out the language specific code for the same. These code-providers reside in the various Microsoft.* namespaces. For instance, the C# code-provider resides under the Microsoft.CSharp namespace, while the VB.NET code-provider resides under the Microsoft.VisualBasic namespace. For the VJ# and Managed C++ code-providers, you will need to reference their respective assemblies before they can be used to produce the code.

For our exemplification, we will produce C# code for our webservice proxy. Thus, we use the Microsoft.CSharp namespace and create an instance of the CSharpCodeProvider class:

// create the C# code provider, get its code generator and produce the webservice

// proxy code.. 

CSharpCodeProvider provider = new CSharpCodeProvider();
ICodeGenerator gen = provider.CreateGenerator();

IndentedTextWriter tw = new IndentedTextWriter(new
StreamWriter("webservice_proxy.cs"));
gen.GenerateCodeFromCompileUnit(ccu,tw,new CodeGeneratorOptions());

From the code-provider, we use the CreateGenerator method to get reference to an ICodeGenerator implementation that will generate the C# code for us. ICodeGenerator is present under the System.CodeDOM.Compiler namespace, so the same must be included in our code.

Next, we can use one of the various GenerateCodeFrom methods of the ICodeGenerator implementation. Since, in our case, the webservice proxy code is represented in a CodeCompileUnit instantiation, we shall use the GenerateCodeFromCompileUnit method. The first argument to this method is the code-compile-unit from which the code will be generated. The second is the reference to a TextWriter implementation that will be used to write the code to the disk. For this purpose, we pass a reference to the IndentedTextWriter object that will be used to write the webservice-proxy C# code to the file, webservice_proxy.cs. The third argument to GenerateCodeFromCompileUnit is the set of code-generation options and we use the default values of the CodeGeneratorOptions object.


Finally...

Once the GenerateCodeFromCompileUnit method is executed, the web-service proxy is generated in the referenced file on the disk. Open the file and you will see that the code produced is no different from the code produced by the WSDL utility that ships with the .NET Framework SDK. And you can see for yourself that it takes just three simple steps to produce such kind of proxy programmatically, and you can have complete control over the grammar in which the proxy should be produced.

Infact, this is the methodology I used to write the winWSDL application that is the GUI way to produce webservice proxy in the language of your choice. Now, you can do the same too!

Download C# source code to the webservice and its proxy-generating client.

25th January, 2004