Overview
In a previous article we began exploring how ADO.NET and NetCOBOL for .NET could be used to access data in a database. We defined the process for connecting to the database, establishing a command and finally accessing and displaying the data. In this article we will access a Stored Procedure to perform a specific task. A stored procedure is basically a series of SQL statements that reside on the database.The procedure could create a new table, retrieve data from one or more tables, update one or more tables or perform many other tasks.
Our example will select employee data from the Employee table of the Pubs database. We will create a WinForm application that will use a drop-down listbox to present the employees in the database. When an employee is selected the information for that employee will be presented. Our WinForm will look as follows:
I would at this time like to thank Brad Bower, a Systems Engineer with Fujitsu Software for his assistance in creating this example. Brad has worked extensively with ADO, NetCOBOL for .NET and ASP.NET and compiled the example in this article.
Repository
In the repository of the program you will see the following statements:
From our previous sample you will remember these statements are used to employ the .NET Framework data access classes. Specifically we are going to use the SqlConnection class to connect to the database, the SqlDataAdapter class to access the data and the DataSet and DataTable to access specific fields in the returned data.
Bind Combo Box to Data
In order to populate the combo box with the employee ID we need to bind the control the specific data item. In the properties for the combo box the property ValueMember has been updated to reflect that data from the column emp_ID will be used to populate the list.
This is an important step in the process. If this is not done the combo box will not be populated with the employees already in the database and we would have to use another method to select the employees.
OBJECT Working-Storage Section
In the WORKING-STORAGE SECTION of the OBJECT we need to define some variables that we will be using in the methods. By defining these items in the OBJECT's WORKING-STORAGE area they will be usable by other methods in the instantiated object. The variables we will define here will contain the data returned from the database and are defined as follows:
Notice that the last field is a date field. In the table definition this column is defined as
a "DATETIME" data type. In order for us to display this information properly on the form we
will convert this to a string, but in order to do this we will need some things added to the REPOSITORY.
The following statements were added to make working with the DATETIME variable easier:
CLASS CLASS-DATETIME AS "System.DateTime"
PROPERTY PROP-DAY AS "Day"
PROPERTY PROP-MONTH AS "Month"
PROPERTY PROP-YEAR AS "Year"
"NEW" Method
When the form is created the method "NEW" is called. It is here we will want to place our code to establish our connection to the database, retrieve some data and populate the drop-down box with employees. Remember we want the form to be displayed with the combo box already populated, not having to populate it after the form is displayed.
To begin with we need to create some WORKING-STORAGE variables to hold the objects we will be creating, or instantiating. Therefore, in the WORKING-STORAGE SECTION of the "NEW" method we have added the following:
We have created references for a connection, an adapter, a dataset and a data table. We have also created a connection string variable of 200 characters in length that will be used to pass the connection string to the connection invocation. It may seem like an unnecessary step but as we will see in the Procedure Division of the method in a minute, it does make the code a bit easier to read and follow.
In the original Procedure Division for the method "NEW" there was one line to invoke the method "InitializeComponent". That line has been left in place and additional coding has been implemented to establish connectivity with the database. All the new coding has been placed after the "InitializeComponent" line. This is because we must have the form and all of it's control and properties initialized and available before we can proceed. The updated "NEW" method thus appears as follows:
Notice after the connection has been established and the data retrieved the combo box is populated. Let's take a minute and look at how the combo box is populated. The first line of the code instantiates a new dataset. This will be to hold the data.
INVOKE CLASS-DATASET "NEW" RETURNING MySQLDataSet.
The next line of code, using the data adapter object that was created earlier, populates or fills the data set with data from the table "Employee" in the PUBS database (remember in the connection string we told it what table to access).
INVOKE MyDataAdapter "Fill" USING MySQLDataSet "Employee".
Up to now we have been working with complete tables. We now need to access one item in the table, a specific column, and bind it to the combo box. We do this by using the "get_Item" method of the data set. We access the property "Table" and tell it we want to create a data table of only the first data item in the table, no matter how many rows. We do this with the following statement:
INVOKE PROP-TABLES OF MySQLDataSet "get_Item" USING BY VALUE 0 RETURNING MySQLDataTable.
We invoke the property "Tables" of the data set MySQLDataSet calling the method "get_item" telling it to access the data in the first position (or column) and return a SQL Data Table. Remember, .NET is zero based so the first item in a table is Zero, not one. While the process may seem confusing at first, re-read this section a few times and look at the code in Visual Studio. Take the time to understand what each line is doing and why. As always check the Help section for further information on the statements and review the class members for each of the classes mentioned. This is new and does take a bit of getting used to.
Now that we have a data table of just employee ID's we need to inform the combo box where to look to get it's data. The following line of code updates the property of the combo box to tell it the data source is the SQL Data Table populated in the previous line of code.
SET PROP-DATASOURCE OF cboEmp TO MySQLDataTable.
Combo Box Updates
If you were to select the combo box control and double click on it you would create a new method called "cboEmp_SelectedIndexChanged". This method is executed when the user selects a different employee from the list presented. This is the method we will use to populate the other data fields on the form.
In the WORKING-STORAGE SECTION for the method the following lines have been added:
The SelID will be used to obtain the "Selected value" property from the combo box. The easiest way to obtain this field is to use an object reference and then convert the object reference to a string. The field sID will be used to receive the employee ID.
The Procedure Division for the method will first obtain the selected value from the combo box and store it in the SelID object. After that has been obtained the method "ToString" from the class "CONVERT" will be invoked using the SelID as an input parameter and returning a string to the sID parameter. The sID parameter will then be used as an index in the call to the GetEmployeeData method to retrieve the data for that employee. The code to do this is:
Stored Procedure
If you will remember from our earlier discussion we will call a stored procedure to get our data. The stored procedure defined below is not a part of the PUBS database. You will need to add this stored procedure to the pubs database in SQL. ( We will not review how to add a Stored Procedure to a table. For information on how to accomplish this please refer to MSDN or work with your DBA).
Now we will create the new method to retrieve the data using sID. This method will be called GetEmployeeData.
GetEmployeeData
The GetEmployeeData method will access the stored procedure and return the columns for the selected index. To begin, we need to establish the method and then some WORKING-STORAGE items that we will use to create the connection, the command and the parameter list of data to be retrieved. The method definition and WORKING-STORAGE section is presented in the following code:
These declarations will allow us to define the parameters of the parameter collection. The parameter collection is as it's name implies, a collection of parameters to be used to interact with the database. The collection has to be built however, it is not an automatic process. You as the designer have to define the parameters based on the task at hand. What do you need to retrieve in order to accomplish your task?
The PROCEDURE DIVISION of the method contains comments about what each line or section of code is doing. We will not replicate the entire method here but rather point out issues that are important to the successful execution of the code.
The first issue is the need to build the parameter list. We have told the stored procedure to return a number of variables to us. These will be returned as parameters within the SQL Command structure. In order to ensure we obtain the necessary information back we need to add each of the returned data items to the parameter list. They are not included in the parameter list by default and must be added individually.
Let's look at two different examples, an input parameter (one which will be used as selection criteria) and an output parameter (one that will be used to return data from the database to the source code):
The general composition of all the parameter blocks is as follows:
- Set the data type to the proper type. Review the data types available within the .NET Framework help system if you have any questions.
SET MySqlDbType TO SqlDbType-Char OF SqlDbType
- Create a new SQL Parameter. The "USING" phrase is comprised of the following parameters: Host variable, data type, and length of the data being returned. The "RETURNING" phrase establishes the connection between the WORKING-STORAGE variable you defined and the SQL Parameter.
INVOKE CLASS-SQLPARAMETER "NEW" USING BY VALUE "@ID" MySqlDbType 9 RETURNING parameterID.
- Instructs the system to set the value of the variable.
SET Value OF parameterID TO sID
- Add the parameter to the parameter list of the command you created in preceding code. You can view the actual instantiation of the command object in the source code provided.
INVOKE Parameters OF MyCommand "Add" USING BY VALUE parameterID.
The only difference between the input section of code described above and an output section of code is the following line:
SET SqlParam-Direction OF parameterEmpID TO ParamDirection-Output OF ParameterDirection.
This line of code establishes the parameter being defined as an "OUTPUT" parameter and data will only flow from the database and thus no updates will be allowed on this field, at least in this procedure.
Once the parameter collection is built we are ready to execute the command object which will populate this collection. We will open the connection then call the ExecuteNonQuery method of the command and then close the connection. The ExecuteNonQuery method does not return any rows, but populates any output parameters or return values mapped to parameters.
INVOKE MyConnection "Open".
INVOKE MyCommand "ExecuteNonQuery".
INVOKE MyConnection "Close".
After successful execution of the command the parameter collection is populated. Now we need to retrieve the values and move them to the WS-EMPLOYEE Group level. As before we will not reproduce the entire section of code as the process is repetitive. Instead we will present one selection of code and detail what it is doing.
The first line of code is setting the object parameterValue to the value of the Employee ID as defined in the parameter list. The parameterValue object was defined in the WORKING-STORAGE section as an "OBJECT REFERENCE CLASS-OBJECT". Instead of creating multiple fields of multiple types to perform this task we used an object reference and then invoked a "ToString" conversion to obtain the data. This was much cleaner coding and reduced the number of variables we defined.
SET parameterValue TO PROP-VALUE OF parameterEmpID.
The next line of code performs a conversion of the parameterValue object to a string and places it in the proper WORKING-STORAGE field.
INVOKE CLASS-CONVERT "ToString" USING BY VALUE parameterValue RETURNING WS-EMPID.
These two lines of code will need to be repeated for each parameter being returned in order to display the data.
The final step is to display the data from the WORKING-STORAGE variables onto the form itself. Each of the fields is presented below showing how to set the text property of the individual text box to the associated WORKING-STORAGE variable.
SET PROP-TEXT OF txtEmpID TO WS-EMPID.
SET PROP-TEXT OF txtFName TO WS-FNAME.
SET PROP-TEXT OF txtMInit TO WS-MINIT.
SET PROP-TEXT OF txtLName TO WS-LNAME.
MOVE WS-JOB-ID TO WS-VALUE.
SET PROP-TEXT OF txtJobID TO WS-VALUE.
MOVE WS-JOB-LVL TO WS-VALUE.
SET PROP-TEXT OF txtJobLvl TO WS-VALUE.
SET PROP-TEXT OF txtPubID TO WS-PUB-ID.
SET PROP-TEXT OF txtHireDate TO WS-HIRE-DATE-A.
By doing this exercise you can see how easy it can be to use the power of SQL and COBOL together to return data to a group level item thus allowing you to use existing code that may have been written for a different data source such as indexed files. This will help you to maintain existing, tested code while only having to write a thin access layer to handle your file I/O.
Happy Coding!