Delphi Clinic C++Builder Gate Training & Consultancy Delphi Notes Weblog Dr.Bob's Webshop
Dr.Bob's Delphi Notes Dr.Bob's Delphi Clinics Dr.Bob's Delphi Courseware Manuals
 Dr.Bob Examines... #41
See Also: other Dr.Bob Examines columns or Delphi articles

This article originally appeared in the DotNET Developers Group DDG Members' Magazine (May/June 2003)

C# Web Services with only the .NET Framework SDK
In this article, we'll see how web services can be written and consumed in C# and using nothing but the .NET Framework SDK (i.e. without the need for Visual Studio .NET or any other commercial development environment). The example web service in this article is one that I've written a few times before (using other development environments and languages), and is merely used to demonstrate the syntax and techniques using C#.

Web Service Server
You don't need Visual C# .NET to write the Euro42 Web Service in C#, since it's simple in structure. In fact, I didn't even use code behind, so the C# source code is contained in a single file (Euro42.asmx). The first line of this file specifies the language used to implement the web service as well as the class name Euro42 that I'm exporting. I only need to add one namespace in the using clause, namely System.Web.Services. The actual web service itself consists of three methods: an About method, a GuldenToEuro and an EuroToGulden method.
The complete - short - source code for the C# web service can be seen below (placed in file Euro42.asmx):

  <%@ WebService Language="C#" Class="Euro42" %>
  using
    System.Web.Services;

  [WebService(Namespace="http://www.eBob42.com", Description="Dr.Bob's Euro42 Web Service written in C# using ASP.NET")]
  public class Euro42: System.Web.Services.WebService
  {
    [WebMethod]
    public string About()
    {
      return "Dr.Bob's Euro42 Web Service written in C# using ASP.NET";
    }

    [WebMethod]
    public double GuldenToEuro(double Gulden)
    {
      return Gulden / 2.20371;
    }

    [WebMethod]
    public double EuroToGulden(double Euro)
    {
      return Euro * 2.20371;
    }
  }

Note that the declaration of the Web Service class Euro42 is preceded by a line of code between square brackets. This is called an attribute in C#. An attribute specifies some attribute value(s) for the following declaration, which can be a class or method for example. For our Euro42 web service, the attribute WebService specifies the Namespace to use, as well as a short Description of our web service (one which we'll see again in a moment). If you don't specify a value for the Namespace, you'll get a warning that the default namespace is used (which is not a good idea, since anybody can come up with a web service (or types that you define) using the same names, and a unique namespace makes sure that each web service, method and type is truly unique.
If we look inside the web service class definition, we see the three methods. Preceding each method, there is another attribute, this time just the word WebMethod. This is actually all that it takes to "flag" the following method as being the method of a web service, meaning that WSDL for this method (signature) will be generated, and that the method can be invoked by a received SOAP envelope request and will produce a SOAP response as well. You have to admit, it's pretty easy to write a web service using this syntax.

Deployment
To test this C# web service, you only have to place it in a (virtual) directory on your web server that has the scripting rights set. If you've installed IIS on your local machine (recommended by the .NET Framework SDK installation itself), then the C:\Inetpub\Scripts directory is a fine place to use. Inside a browser, you can then enter http://localhost/Scripts/Euro42.asmx to connect to the web service.

Testing the Web Service
If you enter the URL for the web service in a browser, you see the description (remember that attribute we used) as well as the available method:

Ignore the Service Description link for now (we'll use that in a moment), but click on one of the methods to get more information and even the ability to test it.
If you click on the GuldenToEuro method, you see the format of the SOAP request, and also get a little form to test it:

If you enter 100 as value for Gulden and click on the Invoke button, a new pop-up browser window will appear with the SOAP response, containing the converted value 45.37:

Note that you can test the web service on your own local machine before you deploy it (and allow other developers to build web service clients that use this service).

Web Service Client
Now that we have a C# web service - even one deployed on the web - it's time to see if we can use it again. In order to use a web service - also called consuming a web service - we first have to generate a class definition that will resemble the web service (without the implementation) that we can use and call from a client application. Just assume we don't know the implementation details from the Euro42 web service, we have to obtain the specifications somehow. Web Services share their specifications through a formal definition using the Web Service Description Language (WSDL). For our Euro42 web service, this formal description can be seen by using the ?WSDL extension.
First of all, to import the web service definition, we have to use the WSDL utility that comes with the .NET Framework SDK (note that it's not part of the much smaller .NET Framework redistributable - you need the full SDK). Using the WSDL utility, we can import the WSDL (definition) from the web service and produce a C# source file as follows:

  wsdl http://drbob42.europe.webmatrixhosting.net/Euro42.asmx?WSDL

This will produce a source file Euro42.cs that contains the class definition for Euro42 again, but this time derived from System.Web.Services.Protocols.SoapHTTPClientProtocol - a web service client instead of the web service server that we created before. Note the this.Url property which is set in the Euro42() constructor that points to the location of the web service.

  //------------------------------------------------------------------------------
  // 
  //     This code was generated by a tool.
  //     Runtime Version: 1.0.3705.288
  //
  //     Changes to this file may cause incorrect behavior and will be lost if
  //     the code is regenerated.
  // 
  //------------------------------------------------------------------------------

  //
  // This source code was auto-generated by wsdl, Version=1.0.3705.288.
  //
  using System.Diagnostics;
  using System.Xml.Serialization;
  using System;
  using System.Web.Services.Protocols;
  using System.ComponentModel;
  using System.Web.Services;


  /// 
  [System.Diagnostics.DebuggerStepThroughAttribute()]
  [System.ComponentModel.DesignerCategoryAttribute("code")]
  [System.Web.Services.WebServiceBindingAttribute(Name="Euro42Soap", Namespace="http://www.eBob42.com")]
  public class Euro42 : System.Web.Services.Protocols.SoapHttpClientProtocol {

      /// 
      public Euro42() {
          this.Url = "http://drbob42.europe.webmatrixhosting.net/Euro42.asmx";
      }

      /// 
      [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://www.eBob42.com/About", RequestNamespace="http://www.eBob42.com", ResponseNamespace="http://www.eBob42.com", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
      public string About() {
          object[] results = this.Invoke("About", new object[0]);
          return ((string)(results[0]));
      }

      /// 
      public System.IAsyncResult BeginAbout(System.AsyncCallback callback, object asyncState) {
          return this.BeginInvoke("About", new object[0], callback, asyncState);
      }

      /// 
      public string EndAbout(System.IAsyncResult asyncResult) {
          object[] results = this.EndInvoke(asyncResult);
          return ((string)(results[0]));
      }

      /// 
      [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://www.eBob42.com/GuldenToEuro", RequestNamespace="http://www.eBob42.com", ResponseNamespace="http://www.eBob42.com", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
      public System.Double GuldenToEuro(System.Double Gulden) {
          object[] results = this.Invoke("GuldenToEuro", new object[] {
                      Gulden});
          return ((System.Double)(results[0]));
      }

      /// 
      public System.IAsyncResult BeginGuldenToEuro(System.Double Gulden, System.AsyncCallback callback, object asyncState) {
          return this.BeginInvoke("GuldenToEuro", new object[] {
                      Gulden}, callback, asyncState);
      }

      /// 
      public System.Double EndGuldenToEuro(System.IAsyncResult asyncResult) {
          object[] results = this.EndInvoke(asyncResult);
          return ((System.Double)(results[0]));
      }

      /// 
      [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://www.eBob42.com/EuroToGulden", RequestNamespace="http://www.eBob42.com", ResponseNamespace="http://www.eBob42.com", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
      public System.Double EuroToGulden(System.Double Euro) {
          object[] results = this.Invoke("EuroToGulden", new object[] {
                      Euro});
          return ((System.Double)(results[0]));
      }

      /// 
      public System.IAsyncResult BeginEuroToGulden(System.Double Euro, System.AsyncCallback callback, object asyncState) {
          return this.BeginInvoke("EuroToGulden", new object[] {
                      Euro}, callback, asyncState);
      }

      /// 
      public System.Double EndEuroToGulden(System.IAsyncResult asyncResult) {
          object[] results = this.EndInvoke(asyncResult);
          return ((System.Double)(results[0]));
      }
  }

In order to compile the Euro42.cs file, we have to use the csc command-line C# compiler (that comes with both the .NET Framework SDK and the smaller .NET Framework redistributable). Since the Euro42.cs is just an assembly, and not an application, we must call csc with the /t:library option to ensure that the result is an assembly (with the .dll extension):

  csc /t:library Euro42.cs

Once the Euro42.dll assembly is available, we can write a new C# application that uses the Euro42 web service. A very short example application calling the web service methods is as follows (in file UseEuro42.cs):

  using System;
  namespace eBob42
  {
    class UseEuro42
    {
      static void Main(string[] args)
      {
        Euro42 euro = new Euro42();
        Console.WriteLine(euro.About());
        double gulden = euro.EuroToGulden(100);
        Console.WriteLine("100 euro = " + gulden.ToString() + " guldens.");
        Console.WriteLine("100 gulden = " + euro.GuldenToEuro(100).ToString() + " euros.");
      }
    }
  }

As you can see, we create an instance of the Euro42 web service and call the About, EuroToGulden and GuldenToEuro methods. Note that I'm not destroying the Euro42 instance at the end of the application (the .NET garbage collector will take care of that).
To compile this example application, we have to use csc again, this time with the /r:Euro42.dll command-line option to include a reference to the Euro42.dll assembly:

  csc /r:Euro42.dll UseEuro42.cs

And now we have a UseEuro42.exe that can be called and will use the Euro42 web service.

Summary
In this article, I've shown how to write C# web services for .NET with only the .NET Framework SDK. We've seen how to deploy them (and even where you can deploy them for free), and how to import and use them from a C# web service client application again. All without the need for a commercial development environment, although I'm sure C#Builder will add additional functionality in this area that will be helpful for bigger projects (a topic I'll explore in more detail once C#Builder is actual available).


This webpage © 2003-2010 by Bob Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved.