Try OpenEdge Now
skip to main content
Web Services
Developing a .NET Client to Consume OpenEdge SOAP Web Services : Creating .NET DataSets from ProDataSet parameters
 

Creating .NET DataSets from ProDataSet parameters

This section presents an example of how to create an .NET DataSet from a ProDataSet parameter. Because .NET has a proprietary method of recognizing and exposing .NET DataSets in WSDL documents, its toolkit cannot translate the WSDL definition of a ProDataSet directly into an .NET DataSet. To work around this limitation, a .NET client can walk the object arrays and populate a .NET DataSet with the data.
Note: This section does not use RPC/Encoded WSDLs and the VB.NET language. Instead, it uses Doc/Lit and C#.NET.
The following table describes the specifications for the sample Web service and interface used in this section.
Table 48. ProDataSet to .NET sample Web service specifications
Property or component
Value or name
Object type
Web service
CustOrdersService
URL
http://servicehost:80/wsa/wsa1
Session model
Session-Free
TargetNamespace
urn:CustOrders
WSDL objects
CustOrdersObj
AppObject
dsCustOrd
ProDataSet dsCustOrd
In general, the other information presented in the previous example for creating a client interface and developing a client application applies here as well. Except where the differences in the client language and session model affect things, you would complete the same tasks to build this sample.
This Web service uses the following code to create and populate a static ProDataSet parameter:
/* getCustOrders.p */
DEFINE TEMP-TABLE ttCust NO-UNDO
   FIELD CustNum AS INTEGER
   FIELD Name AS CHARACTER
   FIELD Balance AS DECIMAL
   INDEX CustNumIdx IS UNIQUE PRIMARY CustNum.

DEFINE TEMP-TABLE ttOrder NO-UNDO
   FIELD OrderNum AS INTEGER
   FIELD CustNum AS INTEGER
   FIELD OrderDate AS DATE
   INDEX OrderNumIdx IS UNIQUE PRIMARY OrderNum
   INDEX CustOrdIdx IS UNIQUE CustNum OrderNum.

DEFINE DATASET dsCustOrd FOR ttCust, ttOrder
   DATA-RELATION CustOrdRel FOR ttCust, ttOrder
      RELATION-FIELDS (CustNum, CustNum).

DEFINE INPUT PARAMETER iCustNum AS INTEGER EXTENT 2.
DEFINE OUTPUT PARAMETER DATASET FOR dsCustOrd.

DEFINE VARIABLE hq1 AS HANDLE.
DEFINE VARIABLE hq2 AS HANDLE.
DEFINE VARIABLE lret AS LOGICAL.
DEFINE DATA-SOURCE dsCust FOR Customer.
DEFINE DATA-SOURCE dsOrder FOR Order.

/* fill dataset and return to caller */
CREATE QUERY hq1.
hq1:SET-BUFFERS(BUFFER Customer:HANDLE).
lret = hq1:QUERY-PREPARE("for each Customer where CustNum >= " + STRING    (iCustNum[1]) + " AND CustNum <= " + STRING(iCustNum[2])).

DATA-SOURCE dsCust:QUERY = hq1.

/* attach the data-sources to the dataset buffers */
BUFFER ttCust:HANDLE:ATTACH-DATA-SOURCE(DATA-SOURCE dsCust:HANDLE,?,?,?).
BUFFER ttOrder:HANDLE:ATTACH-DATA-SOURCE(DATA-SOURCE dsOrder:HANDLE,?,?,?).

MESSAGE "FILL() " DATASET dsCustOrd:FILL().
The Web service accepts an input array to specify a range of customer numbers. The service then outputs the data in the dsCustOrd ProDataSet.
When you add a Web Reference to the CustOrdersService Web service in Microsoft Visual Studio, the proxies in the Reference.cs file include this method to invoke the getCustOrders operation:
public string
getCustOrders([System.Xml.Serialization.XmlElementAttribute("iCustNum",
    IsNullable=true)] System.Nullable<int>[] iCustNum,
     out dsCustOrd dsCustOrd) {
        object[] results = this.Invoke("getCustOrders", new object[] {
            iCustNum});
        dsCustOrd = ((dsCustOrd)(results[1]));
        return ((string)(results[0]));
}
The Reference.cs file also includes the following partial classes to describe the ProDataSet and its constituent temp-tables:
public partial class dsCustOrd {

    private dsCustOrdTtCust[] ttCustField;
    private dsCustOrdTtOrder[] ttOrderField;
    ...

public partial class dsCustOrdTtCust {

    private System.Nullable<int> custNumField;
    private string nameField;
    private System.Nullable<decimal> balanceField;
    ...

public partial class dsCustOrdTtOrder {

    private System.Nullable<int> orderNumField;
    private System.Nullable<int> custNumField;
    private System.Nullable<System.DateTime> orderDateField;
    ...
To access the Web service, you might build an interface like the following one:
The combo-boxes enable you to set the range of customer numbers for the input parameter. When you click the button, the client calls the Web service, retrieves the data for the specified range, and then displays it in the grid.
As shown in the following code, the button's event logic invokes the Web service passing in the array for the CustNum range and retrieves the requested data:
private void button2_Click(object sender, EventArgs e)
   {
      string result = null;
      int cntr;
      int?[] CustNumRange = new int?[2];

     CustOrders.dsCustOrd dsCustOrd;
     CustOrders.CustOrdersService mySvc =
                                  new CustOrders.CustOrdersService();

      try
      {
         CustNumRange[0] = CustNumLow;
         CustNumRange[1] = CustNumHigh;

         if (CustNumHigh < CustNumLow)
             CustNumRange[1] = CustNumRange[0];

             result = mySvc.getCustOrders(CustNumRange, out dsCustOrd);
Since you know the schema of the incoming DATASET parameter, the event logic can create a matching .NET DataSet to accept the incoming data, as follows:
             // Create a .Net Dataset based on this data
             DataSet x = new DataSet("dsCustOrd");

            DataTable t1 = x.Tables.Add("ttCust");
            DataTable t2 = x.Tables.Add("ttOrder");

             t1.Columns.Add("CustNum", typeof(int));
             t1.Columns.Add("Name", typeof(string));
             t1.Columns.Add("Balance", typeof(System.Decimal));

             t1.Columns[0].Unique = true;

             t2.Columns.Add("OrderNum", typeof(int));
             t2.Columns.Add("CustNum", typeof(int));
             t2.Columns.Add("OrderDate", typeof(DateTime));

             t2.Columns[0].Unique = true;

             DataColumn pCol = t1.Columns[0];
             DataColumn cCol = t2.Columns[1];

             x.Relations.Add(pCol, cCol);
Finally, the event logic fills the DataSet with the data from the incoming DATASET parameter and binds the DataSet to a grid, as follows:
             //Populate the dataset with data from the SOAP message
             GetCustOrders.dsCustOrdTtCust[] ttCust = dsCustOrd.ttCust;
             for (cntr = 0; cntr < ttCust.Length; ++cntr)             {
                Object[] ooCust = new Object[3];
                ooCust[0] = ttCust[cntr].CustNum;
                ooCust[1] = ttCust[cntr].Name;
                ooCust[2] = ttCust[cntr].Balance;
                t1.Rows.Add(ooCust);
             }

             GetCustOrders.dsCustOrdTtOrder[] ttOrder = dsCustOrd.ttOrder;
             for (cntr = 0; cntr < ttOrder.Length; ++cntr)             {
                Object[] ooOrd = new Object[3];
                ooOrd[0] = ttOrder[cntr].OrderNum;
                ooOrd[1] = ttOrder[cntr].CustNum;
                ooOrd[2] = ttOrder[cntr].OrderDate;
                t2.Rows.Add(ooOrd);
             }

             //bind Dataset to Grid
             myGrid.SetDataBinding(x, "ttCust");      }
      catch (Exception ex)
      {
         MessageBox.Show("getCustOrders Failed: " + ex.Message);
      }
   }