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... #110
See Also: Dr.Bob's Delphi Papers and Columns

Consuming Web Services with Delphi Prism
In the previous article, I've created an ASP.NET SOAP Web Service using Delphi Prism, an deployed it on my web server at http://www.bobswart.nl/DelphiPrismWebService/Service.asmx. In this follow-up article, I'll demonstrate how to consume and use this web service using Delphi Prism again.

WSDL
Once the web service is deployed on the deployment web server, we can example the formal defnition, also called Web Service Definition Language or WSDL.For ASP.NET web services, you get the WSDL by appending ?WSDL to the URL of the web service, so that's http://www.bobswart.nl/DelphiPrismWebService/Service.asmx?WSDL for our example.

Note the description, which is placed in a wsdl:documentation tag, including the HTML tags that we added.

Web Service Client
In practice, you will often need to write a client for a Web Service application. This is also possible with Delphi Prism, and is not diretly related to Web Site projects anymore.Importing (or consuming) Web Services can be done in just about any Delphi Prism project target.
For this simple example, let's use a WinForms GUI, so right-click on the solution, select Add | New Project and in the Delphi Prism, Windows category, select a Windows Application.Specify MyWebServiceClient as Name (which will also become the namespace of the new project).
In the new project, right-click on the References node, and select Add Web Reference.In the dialog that follows, we can browse to web services in this solution, connect to web services on the local machine, or use UDDI servers on the local network.

While that's fine during development, it's less useful in practice, since I do not want to write a WinForms application that connects to the local network, but one that actually connects to the deployed web service. So instead of using the local network URLs, I just enter the the URL of our WSDL in the URL textbox and press enter:

Inside the dialog, we now see the human readable description of our service (with HTML tags), including the two methods (and their description, if we had added one). Note that the web reference namespace will be the namespace of our project (MyWebServiceClient) followed by the URL of the domain in reverse order (nl.bobswart.www in this case).

Once we click on the Add Reference button, a new Web Reference node is added to the project, with a subnode called nl.bobswart.www two sub-subnodes called Reference.map and Service.wsdl, with a file Reference.pas as leave node for the Reference.map.This Reference.pas, defining the MyWebServiceClient.nl.bobswart.www namespace, contains the Delphi Prism import of the web service.

In order to use the web service, place a Button control on the Main form, and write the following code (note that the namespace has to be added to the uses clause as well):

  implementation
  uses
    MyWebServiceClient.nl.bobswart.www;

  method MainForm.button1_Click(sender: System.Object; e: System.EventArgs);
  var
    MyService: Service;
  begin
    MyService := new Service();
    MessageBox.Show( MyService.Num2Word(1964) );
  end;
We only have to create a new instance of the Service, and then we can use it and call its methods as if it was a local object.Each call, however, will be passed on from the client to the server.In a synchronous way, by the way. Which means that the application is "frozen" while it's waiting for the call to the web method to return.

Asynchronous calls
Sometimes, a call to a web method can take time - more time than you'd like your application to be waiting (especially since the normal call is synchronous and hence "blocking"). In those cases, you may want to consider calling the web method in an asynchronous way.
The benefit of calling a web method asynchronously is that the application itself does not appear frozen, while waiting for the call to the web method to return. Of course, if the web method is called in response to a button click event, then it would be wise to temporarily disable the button to avoid multiple web method calls to queue up.
In order to call a web method asynchronously, we should not call the method directly, but instead call the Num2WordAsync method. If you check the source code for the Reference.pas unit (generated by importing the web service), you'll notice several methods related to calling the Num2Word method asynchronously:

    method Num2Word(value: System.Int32): System.String;
    event Num2WordCompleted: Num2WordCompletedEventHandler;
    method Num2WordAsync(value: System.Int32);
    method Num2WordAsync(value: System.Int32; userState: System.Object);

    method CancelAsync(userState: System.Object); reintroduce;
The first method is the normal synchronous way of calling Num2Word. There are two Num2WordAsync methods: apart from passing the value to convert, the last one is also passing a userState object. We can use this userState object in the Num2WordCompleted event handler if that's required (for example to assign the result of the conversion to). The event Num2WordComplete will be called when the web method call is completed. We should assign an event handler to this event to respond to the fact that web method is finished.
The last method in the snippet above is called CancelAsync and can be used to cancel any pending asynchronous web method calls.
The easiest way to turn the existing example code into an asynchronous call of the web method is to add a line of code directly after the call to the constructor of Service to assign something to the Num2WordCompleted event.
In the code editor, type "MyService.Num2WordCompleted +=". As soon as you enter a space after the += operator, Code Insight will pop-up with a window to offer a choice of what to assign to this event:

The first choice is a good one, so select Num2WordCompletedEventHandler. As a result, not only will we get an assignment, but a new event handler will be added to the code in the code editor as well, result in the following:

  method MainForm.button1_Click(sender: System.Object; e: System.EventArgs);
  var
    MyService: Service;
  begin
    MyService := new Service();
    MyService.Num2WordCompleted += MyService_Num2WordCompleted;
    MessageBox.Show( MyService.Num2Word(1964) );
  end;

  method MainForm.MyService_Num2WordCompleted(sender: Object;
    e: Num2WordCompletedEventArgs);
  begin
  end;
Inside the newly created event handler, we can now use the e parameter of type Num2WordCompletedEventArgs to get to the result of the web method call. But before we can use that, we should move the reference to the import unit MyWebServiceClient.nl.bobswart.www from the implementation to the interface section (because the event handler declaration in the MainForm class needs this import unit for the Num2WordCompletedEventArgs type).
Then, we can move the MessageBox.Show call to the event handler, showing the e.Result field which contains the result of the web method call.
  method MainForm.MyService_Num2WordCompleted(sender: Object;
    e: Num2WordCompletedEventArgs);
  begin
    MessageBox.Show( e.Result );
  end;
Apart from that, we should also change the synchronous call to Num2Word to the asynchronous call Num2WordAsync. And while we're at it, we should disable the button when the call starts, and enable it again when the call is completed, resulting in the following complete code for the asynchronous example:
  method MainForm.button1_Click(sender: System.Object; e: System.EventArgs);
  var
    MyService: Service;
  begin
    (sender as Button).Enabled := False;
    MyService := new Service();
    MyService.Num2WordCompleted += MyService_Num2WordCompleted;
    MyService.Num2WordAsync(1964);
  end;

  method MainForm.MyService_Num2WordCompleted(sender: Object;
    e: Num2WordCompletedEventArgs);
  begin
    MessageBox.Show( e.Result );
    button1.Enabled := True;
  end;
When you click on the button, it will immediately become disabled (preventing you from clicking again), but the application itself will remain responsive; you can move it around, resize it, click on other controls, etc. until the web method call is completed and the message box will show up (with the button still disabled).
Only after clicking on the OK button in the message box will the button be enabled again, ready to be clicked on again.

The asynchronous way of calling web methods is slightly more work, but will result in more responsive and appreciated user interfaces that do not become "frozen" while the web methods are executing. As a result, I recommend using the asynchronous approach wherever possible.

Summary
In this article, I've demonstrated how we can import and use Web Services in Delphi Prism Applications.I've shown the easy synchronoys way of calling web methods, but also explained the benefits of and demonstrated how to call web methods in an asynchronous way.
Feel free to create and deploy your own ASP.NET Web Services with Delphi Prism, or consume my test Web Services at my website to convert numbers to Dutch words if you like.

For more information on SOAP and Web Services with Delphi, you can check out my Delphi 2006 XML, SOAP and Web Services on Lulu.com, or my RAD Studio 2007 XML, SOAP and Web Services courseware manual in PDF format (with free updates and e-mail support), or wait for the Delphi Prism Development Essentials manual to become available which will also include ASP.NET Web Service coverage.


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