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... #51
See Also: other Dr.Bob Examines columns or Delphi articles

Delphi 8 for .NET and ASP.NET Web Services
In this article, I will show in detail how we can build ASP.NET web services using Borland's Delphi 8 for .NET, as well as how we can import web services (in this case the same one) and use them in WinForms applications - also using Delphi 8 for .NET.

Web Service Engine
The example Web Service that we will be making and using in this article is the Centigrade to Fahrenheit Temperature Conversion Web Service, also known by the name Celsius. This Web Service is already available on the web as http://www.ebob42.com/cgi-bin/Celsius42.asmx, but we'll build a fresh one using Delphi 8 for .NET now.
Start Delphi 8 for .NET, and create a new project using File | New - Other. This will give us the Object Repository, which has three new project targets in the Delphi ASP Projects category: ASP.NET Web Application, ASP.NET Web Service Application, and a Web Control Library (for ASP.NET custom controls - a topic for another time).
Next month, I will cover ASP.NET Web Forms, but right now we'll create an ASP.NET Web Service Application.

Double-click on the ASP.NET Web Service Application icon which starts the NEW ASP.NET Application wizard. In this dialog, we need to specify the name of the project, which will also be the name of the virtual directory. I've entered WebService as name, so we get a project called WebService in a virtual directory called WebService (by default at c:\inetpub\wwwroot\WebService).
We can also use the ASP.NET Web Service Application wizard to select a web server, with a choice between Microsoft's Internet Information Server (IIS) and the free Cassini web server. Cassini is included with Delphi 8 for .NET in the Demos\Cassini directory, and is a good environment for local testing. For more configuration information about Cassini, I recommend Nick Hodges' instructions on getting Cassini to run.

Click on OK to start the new ASP.NET web service project. It consists of a project file WebService.bdsproj, WebService.cfg and WebService.dpr, and contains the files WebService1.asmx, WebService1.pas, Global.asax, Global.pas, and Web.config. We only need to concern ourselves with the WebService1.asmx and .pas file at this time, which are already opened in the code editor. Do File | Save As to save then under a new name, such as Celsius. Note that both files will be renamed at once - so we end up with Celsius.asmx and Celsius.pas.

TCelsius Web Service
Apart from renaming the WebServices1 files to Celsius, I also want to rename the default name of the web service class from TWebService1 to TCelsius. Click on the tab for the Celsius.pas code behind source file. This file contains the web service class in the namespace WebService as specified in Celsius.asmx. By default that class is called TWebService1, and you have to rename it to TCelsius now (a global search and replace is the fastest way). As soon as you save the .pas file, the .asmx file will be updated as well.

Namespace
Apart from the new name of the web service, there is one thing that you may want to add: the web service namespace information (otherwise a web service will use the default namespace http://tempuri.org which is not a good idea since your web service and types should be unique - hence the need for a unique namespace). I always use http://eBob42.org as namespace, so just before the line which declares the web service class, I always add a WebService attribute as follows:

  [WebService(Namespace='http://eBob42.org',
              Description='Celsius Web Service written in Delphi 8 for .NET')]
  TCelsius = class(System.Web.Services.WebService)
    ...
  end;
This is enough to make sure that the TCelsius web service uses a non-default namespace. It's still an empty web service, however. If you browse through the Celsius.pas source file, you'll notice some helpful comments at the bottom of the file that contain a sample HelloWorld method that you can uncomment to use. The most important thing that they should tell you is that any method that must be exported (as web service method) must be preceded by an attribute [WebMethod] in the definition of the method (i.e. inside the TCelsius class definition). Note that HelloWorld (in comments) also has the WebMethod attribute at the implementation, which is not needed there!
Anyway, we don't need the sample method HelloWorld, so just delete it and instead add two new methods to TCelsius: Celsius2Fahrenheit and Fahrenheit2Celsius. The two methods have a double argument and a double result type, converting one temperature value to another. The method definition inside TCelsius is as follows:
  [WebMethod]
  function Celsius2Fahrenheit(degrees: Double): Double;
  [WebMethod]
  function Fahrenheit2Celsius(degrees: Double): Double;
The implementatation code that you can add to the implementation section of the Celsius.pas file is as follows:
  function TCelsius.Celsius2Fahrenheit(degrees: Double): Double;
  const
    AbsoluteZeroCelsius = -273.15;
  begin
    if (Degrees < AbsoluteZeroCelsius) then
      raise ApplicationException.Create('Invalid Temperature');
    Result := 32 + (9 * degrees / 5)
  end;

  function TCelsius.Fahrenheit2Celsius(degrees: Double): Double;
  const
    AbsoluteZeroFahrenheit = -459.67;
  begin
    if (Degrees < AbsoluteZeroFahrenheit) then
      raise ApplicationException.Create('Invalid Temperature');
    Result := 5 * (degrees - 32) / 9
  end;
Once you've implemented these methods, you can test the web service from the Delphi 8 for .NET IDE on your development machine. By testing, I mean using the ASP.NET testing environment. The ASP.NET web service itself can also be debugged from the Delphi 8 for .NET IDE, using Run | Run. However, if you do not need the debugger, it's much faster to start the web service (inside the default browser) using Run | Run Without Debugging.
Apart from that - on some machines, you may get an error message if you attempt to do Run | Run on an ASP.NET project. The problem is also mentioned in the readme.txt (you also always read the readme.txt, right?) in the ASP.NET section. To cut a long story short: the error message "Unable to attach to ASP.NET worker process (typically for aspnet_wp.exe or w3wp.exe)" can often (but not always) be solved by registering ASP.NET with IIS using the following command:
  <WINDIR>\microsoft.net\framework\v1.1.4322\aspnet_regiis.exe -i
Where <WINDIR> stands for your Windows directory (like WinNT or Windows - for your sake I hope it's not Win95, Win98 or WinME ;-)
Anyway, to run the project without actually debugging it, do Run | Run Without Debugging. This will launch Internet Explorer and show you the information about the Celsius web service.
You can even test the methods of the web service in the browser (all facilitated by the ASP.NET environment).

An even more useful test of the web service will be to actually import and use it - in another Delphi 8 for .NET application, of course. So close the browser, close the project (save everything if you haven't done that before), and start another Delphi 8 for .NET project.

Deployment
If you want to use the WebService you've just built, you first need to deploy it on the target deployment web server machine. This web server machine must obviously support ASP.NET 1.1. In your webspace, you need to (ask your ISP to) create a new virtual directory with the Read and Run Scripts rights set, but no Execute, Write or Browse rights are needed. In this virtual directory, you need to upload the Celsius.asmx file as well as the Web.config file. The Global.asax is optional. You also need to create a Bin subdirectory (which doesn't need any special rights), and in this subdirectory you need to deploy the WebService.dll code behind assembly.

Web Services Client
I now want to show you how Delphi 8 for .NET can import and use (also called consume) web services. You can use the example web service that was made in this article, or use another web service available on the web. For your convenience, I have made some web services available on the internet, including the Celsius conversion web service itself, so you can still play along if you want.
Anyway, start Delphi 8 for .NET and build a new project by doing File | New - Windows Form Application. Before we add visual components to the empty form, however, I first want to import the web service and add it to our project (so at least we know the class and methods that we can use in the client application).

Add Web Reference
In order to import a web server, we have to add a web reference (that's how it's called in Delphi 8 for .NET), using the Project | Add Web Reference dialog.

This dialog consists of a URL editbox and browser window, and starts with the Borland Delphi 8 for .NET Universal UDDI Browser to UDDI Services - in case you want to import a web service from one of the available UDDI directories.

Although some of my web services are registered with XMethods, I want to use the Celsius web service that was written earlier by me. In case you have the Celsius web service available on your local machine, the URL to enter in the editbox is most likely http://localhost/WebService/Celsius.asmx. But you can also use the version that I've deployed on the internet as http://www.eBob42.com/cgi-bin/Celsius42.asmx. In both cases, the dialog should now show the information of the Celsius web service.
Apart from the human readable description of the web service, and the list of methods, this page also contains a link to the "Service Description" (which points to http://www.eBob42.com/cgi-bin/Celsius42.asmx?WSDL). If you click on this link, the WSDL (Web Service Description Language) will be shown in the browser.

When you click on the Add Reference button, Delphi 8 for .NET will take a few seconds to analyze the WSDL and produce additional files that are added to the project. Since I've imported the Celsius web service from my www.eBob42.com website (where it also has a method called About), we get a new node com.ebob42.www, with subnodes for the Celsius.wsdl file and a Celsius.map file that refers to the imported web service itself. The import code is placed in ebob42.Celsius.pas, where the class Celsius is defined in the ebob42.Celsius namespace.
The imported source code is interesting to view, but the only interesting part is the class TCelsius with a default constructor, and the methods About, Celsius2Fahrenheit and Fahrenheit2Celsius that we can use very easily.

Client Form
Time to start some visual programming on the WinForm. Drop two Label, two TextBox, and two Button components on the form. Call the first TextBox tbCelsius and the second one tbFahrenheit, and call the first Button btnCelsius2Fahrenheit and the second one btnFahrenheit2Celsius. Now, put "Celsius: " in the Text property of the first Label, and "Fahrenheit: " in the Text property of the second Label. Similarly, put "Celsius 2 Fahrenheit" in the Text property of the first Button, and "Fahrenheit 2 Celsius" in the Text property of the second Button. If you want everything to appear clean, you can also clear the Text property of the two TextBoxes.
Time to write some event handling code for the two Buttons. First, add the ebob42.Celsius unit to the uses clause. Now we can implement the two event handlers. In both cases, we need to create a new instance of the TCelsius class, and then call the required methods.

  procedure TWinForm.btnFahrenheit2Celsius_Click(sender: System.Object;
    e: System.EventArgs);
  var
    C2F: TCelsius;
  begin
    C2F := TCelsius.Create;
    tbCelsius.Text :=
      C2F.Fahrenheit2Celsius(Convert.ToDouble(tbFahrenheit.Text)).ToString
  end;

  procedure TWinForm.btnCelsius2Fahrenheit_Click(sender: System.Object;
    e: System.EventArgs);
  var
    C2F: TCelsius;
  begin
    C2F := TCelsius.Create;
    tbFahrenheit.Text :=
      C2F.Celsius2Fahrenheit(Convert.ToDouble(tbCelsius.Text)).ToString
  end;
Note that you should probably want to place all code inside a try-catch block, since creating an instance of the web service can fail (if you cannot connect to it for some reason), but even if you have created an instance, the call to Celsius2Fahrenheit or Fahrenheit2Celsius can also fail or throw an exception - for example when trying to convert a temperature which is below the absolute zero. You can experiment and discover this in practice for yourself.
Now, do File | Save All to make sure everything is saved, compile and run the project which will show the Celsius Client. As quick demo, I've entered 42 in the tbCelsius TextBox and clicked on the bntCelsius2Fahrenheit button:

Error Handling
When entering a value of -400 in the TextBox for Celsius, and then trying to convert from Celsius to Fahrenheit will result in an exception being thrown by the web service. Another exception will be raised if the web service isn't available. Using try-except will make sure that the client application can intercept the exception and optionally present some more useful information to the endusers as follows:

  procedure TWinForm.btnFahrenheit2Celsius_Click(sender: System.Object;
    e: System.EventArgs);
  var
    C2F: TCelsius;
  begin
    try
      C2F := TCelsius.Create;
      tbCelsius.Text :=
        C2F.Fahrenheit2Celsius(Convert.ToDouble(tbFahrenheit.Text)).ToString
    except
      on E: Exception do
        MessageBox.Show(E.Message)
    end
  end;

  procedure TWinForm.btnCelsius2Fahrenheit_Click(sender: System.Object;
    e: System.EventArgs);
  var
    C2F: TCelsius;
  begin
    try
      C2F := TCelsius.Create;
      tbFahrenheit.Text :=
        C2F.Celsius2Fahrenheit(Convert.ToDouble(tbCelsius.Text)).ToString
    except
      on E: Exception do
        MessageBox.Show(E.Message)
    end
  end;
Obviously, it would be a good idea to give a more user-friendly error message in the except-clause, but I leave that as exercise for the reader.

Summary
In this article I've used Delphi 8 for .NET - Borland's new IDE for Microsoft's .NET Framework - to build, implement, test, deploy and finally consume (import and use) an example ASP.NET web service. We've seen numerous IDE features that support web service developers and consumers while working with ASP.NET web services in Delphi 8 for .NET.
Next month, we will cover ASP.NET Web Forms with Delphi 8 for .NET.


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