Consume an IWS Web Service From a VB.NET Client
January 13, 2010 Michael Sansoterra
Note: Source code for RPG program CustListR can be downloaded here.
In my article Publish Result Sets Using Web Services and IWS, I demonstrated how to write an RPG program that can return a result set (i.e., multiple rows of data) to a Web service client using an Integrated Web Services (IWS) server instance.
The benefit of using RPG to publish data as a Web service is that any number of clients can consume the Web service, regardless of OS platform or language. This potentially makes your i data and business logic accessible from anywhere! This tip will continue where the prior one left off by demonstrating how to consume the sample CUSTLISTR Web service using a .NET client in the context of an ASP.NET Web page.
If you haven’t already reviewed the tip on how to publish a result, please review it now. There is also an important discussion on some of the current limitations that may persuade you against publishing Web services using IWS.
This tip’s example will be done using Visual Studio 2008 Professional Edition (although other editions should work). This tip assumes you know the basics of creating and editing Visual Studio projects. For demonstration purposes, I created a simple VisualBasic ASP.NET Web Application Project. I then added a Web page called DEFAULT.ASPX and dropped a GridView control on it called GridView1. The idea will be to see the results of the Web service call (a list of customers from RPG program CustListR in tabular format) on a Web page.
A Refresher on RPG Program CustListR
As a brief recap, the last article revolved around RPG program CustListR, which was designed to receive an SQL Where clause string, build a SQL statement to retrieve customer information based on the received Where clause, execute the SQL statement, and return a list of customers as an output parameter.
The program’s parameter list is shown below:
Table 1–Parameters for the CustListR program.
This RPG program was then deployed as a “Web service” using the new IWS server so that just about any client written in just about any language and deployed on just about any platform can access it. As discussed in that tip, deploying an RPG program as a Web service on an IWS server is as simple as a walking through a few steps in the deployment wizard.
A Little Background on .NET Client Options. . .
Microsoft offers two methods for consuming Web services: using Windows Communication Foundation (WCF) or the original Web service reference implementation. Which one you’ll use in your projects depends on a number of factors. Here, I’ll simply demonstrate how to use each to consume the RPG Web service. Either way, the good news is that Visual Studio does most of the heavy lifting for developers by automatically creating classes that can consume a Web service written in RPG or any other language.
How does Visual Studio do this? Recall that each IWS Web service is published with a corresponding WSDL (Web Services Description Language) document. This WSDL document (published in XML format) defines the parameters for the Web service and the operation(s) to be executed. Visual Studio simply reads the WSDL file and automatically creates code based on the parameter names, etc., it finds in the WSDL document.
As discussed in the prior article, the WSDL document (and its URL) generated for a Web service published on an IWS server can be accessed from the IBM http server administration page. You’ll want to know the URL to each Web service WSDL document you’ve published so that you can supply them to clients that access the Web service.
And now on to the brief tutorial demonstrating how to access the CustListR RPG program (defined as a Web service) from a .NET client using either a Web Service Reference or a WCF Service Reference.
Add a Web Service Reference
Once you have a Visual Studio project, add a Web service reference to your project by choosing Project→Add Web Reference. If this option isn’t available from the Project menu, you can alternatively right-click on the project name in the Visual Studio Solution Explorer and choose it there.
When the “Add Web Reference” dialog appears, enter the URL to your Web service’s WSDL document and a friendly “Web reference name.” (See Figure 1.) The friendly name will be referred to in the .NET code and should be a name that is representative of one or more Web service operations that are present in the given WSDL document (i.e., InventoryServices or FinanceServices).
Here are sample values to plug into the prompts:
Web Reference Name: MyWebService
Of course your port number and host name will vary. When you click the “Add Reference” button, Visual Studio will read the WSDL document and automatically generate the class definitions required to invoke the Web service from your project.
Now that Visual Studio has created classes to reference this Web service, we can make use of them. To do this, just add a Web Form (ASPX Web page) to your project (Project→Add Component→Web Form) and drop a GridView control on the form. In this project, my Web form is called Default.aspx. We’ll populate the GridView control from the result set returned by the Web service (i.e., RPG program).
Assuming a friendly name of “MyWebService” was specified, Code 1 below shows the sample VB.NET code required to bind the Web service data to the read only GridView (which is named GridView1 in the code). This code is placed in the Web form’s Page_Load event:
' ' Define CUSTOMERLIST Web Service Reference ' Dim client As MyWebService.CustomerList = New MyWebService.CustomerList() ' ' Define Input parameters (WHERE clause) ' Dim input As MyWebService.CUSTLISTRInput = New MyWebService.CUSTLISTRInput input._PARMWHERE = "Where TerritoryID=4" ' ' Define Result object to handle output parameters, ' and invoke web service using input parameters ' Dim result As MyWebService.CUSTLISTRResult = client.custlistr(input) GridView1.DataSource = result._DSCUSTOMERS GridView1.AutoGenerateColumns = True GridView1.DataBind()
Code 1–Sample VB code for binding the CustListR Web service results to a GridView control using a Web service reference.
Let’s face it, that’s not much code to tie the two together! Three variable declarations related to the Web service are described below.
The first variable definition is named “client” and is defined with auto-generated type MyWebService.CustomerList (the friendly name followed by the IWS service name). It is used to execute the call to the Web service. Before we can execute the call, we’ll need variable “input” to hold the Web service’s input parameters and variable “result” to contain the Web service’s customer array results (more on this in a moment). This auto-generated “client” class has method custlistr, which is used to execute the Web service call. Method custlistr inherits its name from the WSDL document’s operation name. Each operation in the WSDL document basically maps to a program name when working with a program object or a subprocedure/function name if your Web services are based on a service program. Because this Web service call requires input parameters, the CustomerList method requires the appropriate “input” object containing the required parameter values to be passed.
Variable “input” defined as auto-generated type “MyWebService.CUSTLISTRInput” has member “_PARMWHERE” that will be passed to the “parmWhere” parameter of RPG program CustListR. The initial underscore and the capitalization of the parameter are all taken from the WSDL that is generated when the Web service is deployed using the wizard. In the sample code above, _PARMWHERE is hard-coded with an SQL Where clause string but obviously this is something that would normally be built “on the fly” based on some criteria that the user chooses on the Web page.
Finally, variable “result” is defined as the auto generated CUSTLISTRResult type and is defined to receive the list of customers returned from the Web service. The “result” contains member “_DSCUSTOMERS” that are simply a structure array that matches the CUSTLISTR RPG program’s dsCustomers output parameter (a data structure array). Again, the prefixed underscore and capitalization are actually taken from the WSDL generated when the program is deployed as a Web service on an IWS server.
Once the array of customers has been populated in the “result” variable, set the GridView’s data source property to the _DSCUSTOMERS member of “result” and then call the GridView’s bind method to render the data on the page.
With a little formatting of the data grid, the results will look something like this:
You now have a Web page bound to the data returned from the RPG program using a Web service reference. Next, we’ll redo the same exercise using a WCF service reference in the Visual Studio project.
Add a WCF Service Reference
Introduced in the .NET 3.0 Framework, Windows Communication Foundation (WCF) is Microsoft’s latest programming interface for building service-oriented applications. WCF offers more flexibility in security options and service “end points” than the original Web services tools available in prior versions of Visual Studio and the .NET Framework. However, in the simple case of accessing the sample Web service RPG program CustListR, there isn’t much of a difference between the two.
To add a WCF Web service reference to your project, choose Project→Add Service Reference. When prompted with the “Add Service Reference” window, enter the URL to the “i” Web service’s WSDL document in the address box and enter a Namespace in the Namespace box. For this example, I entered iSeries.Customers for my Namespace. Click the “Add Reference” button to have Visual studio create the classes to call this “i” Web service.
Once the code to call the Web service is generated, here again is VB code (Code 2) showing how to execute the Web service and bind the results to a grid view control on a Web form. This code assumes the service is assigned a namespace called iSeries.Customers:
' ' Define WCF Client ' Dim wcfClient As iSeries.Customers.CustomerListPortTypeClient = New iSeries.Customers.CustomerListPortTypeClient() ' ' Define Object to accept input parameter(s) for the web service ' Dim wcfInput As iSeries.Customers.CUSTLISTRInput = New iSeries.Customers.CUSTLISTRInput() wcfInput._PARMWHERE = "Where TerritoryID=4" ' ' Execute the web service operation and retrieve the output parameter(s) ' in the wcfResult object ' Dim wcfResult As iSeries.Customers.CUSTLISTRResult = wcfClient.custlistr (wcfInput) ' ' The _DSCUSTOMERS data structure ' GridView1.DataSource = wcfResult._DSCUSTOMERS GridView1.AutoGenerateColumns = True GridView1.DataBind()
Code 2–Sample VB code for binding the CustListR Web service to a GridView control using a WCF service reference.
As in the previous example, there are three auto-generated classes required to call the IWS Web service: a client (controlling the execution of the Web service), an input class (representing the Web service’s input parameters), and an output class (representing the Web service’s output).
When using WCF with a Web service that can return a large amount of data, you’ll need to increase the amount of data that can be passed in a single Web service call. To do this, open the Web.config file and change the following attributes of the binding element:
If this isn’t done, when over 64K of data (the default) is exchanged between the client and server the following error message will be shown:
The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.
Increase the size of these attributes to the largest amount you’ll possibly need for the Web service. If you’re thinking the V5R4 version of the RPG program should never return more than 64K of data, you’re correct. But don’t forget that the RPG program’s response is put in XML format that considerably bloats the amount of information exchanged between the client and server. A good idea when dealing with varying length messages is to make the Web service return the largest message size possible to make sure the buffer and message size attributes are set correctly.
If you’re thinking at this point that Web services require extra overhead, you are correct. The trade-off is ease of data exchange vs. performance and proprietary communication mechanisms. However, remember the large-scale abandonment of assembly and C in favor of programming higher-level languages and the transition from using indexed files to SQL databases, so too Web services are poised to become the communication mechanism of choice as hardware and network bandwidth advances are made.
Thanks to tools like those offered in Visual Studio, consuming a Web service published by an IWS server on the i is almost trivial with a .NET application. The same holds true for many other environments. The great thing about building applications with services is that there is no specific middleware (Windows OLE DB Provider, Linux ODBC Driver, JDBC driver, etc.) required to sit between the client and server. Implementing Web services allows an IBM i shop to expand its horizons to a whole new world of integration possibilities.