
Dr. Ernest Bonat, Ph.D. founded Visual WWW in 2000. Visual WWW is committed to providing high-quality software business applications and establishing long-term relationships with our clients. We specialize in the design, development, test and implementation of database business applications using Microsoft / Oracle / IBM DB2 / Open Source (LAMP) technologies, including PC-based, Client/Server and Internet web applications.
Ernest is a pioneer in Visual Basic windows development and has been using it since version 1.0 for DOS in 1990. Ernest has more than 20 years experience in computer software development, mathematical modeling and simulation of electronic and microelectronic devices, and manufacturing technological processes, starting from Fortran IV and Basic in 1980, and until today with Microsoft .NET technology. Sometimes he's awake at 4:00 a.m. on Saturday and Sunday mornings working on Visual Basic .NET programming optimization solutions and best practices for his clients and friends.
By Ernest Bonat, Ph.D.
Visual WWW, Inc.
Introduction
Required Software
LoopUp Table Data Loading
Generic ListItemClass Class
LoopUp Project Example
Getting Selected ID and Name Values
Finding ID and Name Values
Finding the State and Capital Names by Postal Code
Executing Stored Procedures in MySQL 5.0/VB.NET 2005
Conclusions
Lookup tables contain, in general, a fixed list of data. This data doesn’t change very often in database business applications. Examples of this data could be a product list, category type, supplier list, state name, zip code, phone area code, etc. In Windows and Internet web business applications, most of these lookup tables are graphically implemented by using ComboBox, ListBox or CheckListBox read-only controls. These controls are loaded with data using two main columns, ID and Name. For example, the USA state table, the ID could be ‘CA’ and the Name ‘California’. Some times, for standard Windows form and Internet web page we need to show data to the end-users from many of these lookup tables. A fast data loading process and defining the main column values for each lookup table is required. In this article I will show you standard lookup data loading procedure and the generic classes object to store and read-only the values of the ID and Name columns from the lookup tables. Selecting and finding the ID and Name values will be provided. Executing stored procedures with input/output parameters in MySQL 5.0/VB.NET 2005 will be covered in detail too.
Let’s look for a simple way to load the data in a ComboBox from a lookup table. Because we may have many lookup tables, it makes sense to develop a generic class for data loading from these tables. Listing 1 shows the structure of the LookUpClass public class within the LoopUpLibrary Namespace. The MySql.Data.MySqlClient library has been imported to reference the Connector/NET 5.0.6. The standard public class ObjectDisposeClass was included for releasing .NET unmanaged resources. Inside the LookUpClass class body we will develop our custom properties, methods and events.
Imports MySql.Data.MySqlClient
Namespace LoopUpLibrary
Public Class LoopUpClass
Inherits ObjectDisposeClass
' Developed custom properties, methods and events
End Class
#Region " IDisposable Object ..."
Public Class ObjectDisposeClass
Implements IDisposable
Private disposedValue As Boolean = False
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: free unmanaged resources when explicitly called
End If
' TODO: free shared unmanaged resources
End If
Me.disposedValue = True
End Sub
End Class
#End Region
End Namespace
Listing 1: LookUpClass class structure
The LoopUpDataLoad() subroutine is shown in Listing 2. This subroutine has two main input parameters, the control to be loaded (pComboBox) and the stored procedure (pStoredProcedureString) that contains the SQL Select statement of the Lookup table. Some user stored procedures examples will be explained late in this article (Listing 5 and 13). As we know, MySQL Server 5.0 introduces, for the first time, the capability of development stored procedures, functions, triggers and views database objects. Because of that, instead of passing a dynamic SQL Select statement, the stored procedure name is passed by value to the subroutine. Stored procedures offer several distinct advantages over dynamic (embedding) SQL in your application code. In the future, I’m going write a single paper about stored procedures development and implementation using MySQL 5.0/VB.NET 2005. I can see many Open Source application developers still using dynamic or/and parameterized SQL statements today with MySQL Server 5.0. It seems to me that they don’t know how to design and develop stored procedures using SQL:2003 language. Just in case, a very good reference book about this topic was published in 2006 by O'Reilly Media, Inc., ISBN: 0-596-10089-2, “MySQL Stored Procedure Programming” by Guy Harrison and Steven Feuerstein. I highly recommend reading this book for any Windows or Internet web application development with MySQL 5.0 database engine.
Public Sub LoopUpDataLoad(ByVal pComboBox As ComboBox, _
ByVal pStoredProcedureString As String, _
ByRef pErrorMsgString As String)
Dim IDString, NameString As String
Try
Using mMySqlConnection As New MySqlConnection(mMySQLConnectionString)
mMySqlConnection.Open()
Using mMySqlCommand As New MySqlCommand
With mMySqlCommand
.Connection = mMySqlConnection
.CommandType = CommandType.StoredProcedure
.CommandText = pStoredProcedureString
End With
Using mMySqlDataReader As MySqlDataReader = _
mMySQLCommand.ExecuteReader(CommandBehavior.SingleResult)
With pComboBox
.Items.Clear()
If mMySqlDataReader.HasRows Then
.BeginUpdate()
While mMySqlDataReader.Read()
IDString = mMySqlDataReader.Item(0).ToString()
NameString = mMySqlDataReader.Item(1).ToString()
If IDString.Length > 0 And NameString.Length > 0 Then
Using mListItemClass As New ListItemClass(NameString, IDString)
.Items.Add(mListItemClass)
End Using
End If
End While
.EndUpdate()
.SelectedIndex = 0
End If
End With
End Using
End Using
End Using
Catch exErr As Exception
pErrorMsgString = exErr.Message
End Try
End Sub
Listing 2: Generic data loading procedure from lookup tables
So far at this point, for the available MySQL Connector/NET 5.0.6, the fastest way to retrieve data from the MySQL Server 5.0 is by using the data reader object MySqlDataReader. XML implementation and data retrieval from MySQL database server is not available at this time. We hope to have these technologies implemented in future releases of MySQL, as Microsoft (SQL Server), Oracle (Oracle Server) and IBM (DB2 Mainframe Server) already have done. As the help file said (C:\Program Files\MySQL\MySQL Connector Net 5.0.6\Documentation\MySql.Data.chm) the data reader object provides a means of reading a forward-only stream of rows from a MySQL database. This object is created by calling the ExecuteReader() method of the command object MySqlCommand as shown in the code (Listing 2). By reading the mMySqlDataReader object, we can get the postal code (IDString) and the state name (NameString). After these two values are determined, they need to be passed to the generic class constructor ListItemClass (Listing 3) and than the entire object will be adding to the item collection of the ComboBox. I would like to mention two new methods implemented in ComboBox control in VB.NET 2005, BeginUpdate() and EndUpdate(). The BeginUpdate() method prevents the control from repainting until the EndUpdate() method is called. In this case the user will no see the flicker during the drawing of the ComboBox when the items are being added to the list.
The ListItemClass class (Listing 3) was created to store and read-only the values of the ID and Name columns from the lookup tables. In general the Name column is defined as a character data type. The values of this column will be stored in mFieldNameString variable by using the class constructor. The read-only property FieldName() will retrieve these values. The ID column could be a character or a numeric data type, depend on the table definition. In this case, the class implements two read-only properties, one for character data type FieldIDString() and another for numeric FieldIDInt32(). This generic ListItemClass class should be included in any Windows or Internet web application project and can be used with any lookup control as ComboBox, ListBox or CheckListBox.
Public Class ListItemClass
Inherits ObjectDisposeClass
Private mFieldNameString As String
Private mFieldIDString As String
Private mFieldIDInt32 As Int32
Public Sub New(ByVal pFieldNameString As String,
ByVal pFieldIDInt32 As Int32)
mFieldNameString = pFieldNameString
mFieldIDInt32 = pFieldIDInt32
End Sub
Public Sub New(ByVal pFieldNameString As String,
ByVal pFieldIDString As String)
mFieldNameString = pFieldNameString
mFieldIDString = pFieldIDString
End Sub
Public Sub New()
mFieldNameString = Nothing
mFieldIDString = Nothing
mFieldIDInt32 = Nothing
End Sub
Public ReadOnly Property FieldName() As String
Get
Return (mFieldNameString)
End Get
End Property
Public ReadOnly Property FieldIDInt32() As Int32
Get
Return (mFieldIDInt32)
End Get
End Property
Public ReadOnly Property FieldIDString() As String
Get
Return (mFieldIDString)
End Get
End Property
Public Overrides Function ToString() As String
Return (mFieldNameString)
End Function
End Class
Listing 3: Generic ListItemClass class
To show a real example of lookup data loading I created a simple VB.NET 2005 solution project shown in Figure 1. The two ComboBoxes are loaded with USA States and Capitals respectively.

Figure 1: ComboBox data loading for USA States and Capitals
The load event of the VB.NET Form is shown in Listing 4. As you can see, both ComboBoxes, the States (StatesComboBox) and Capitals (CapitalsComboBox) are loaded using the same LoopUpDataLoad() subroutine shown above in Listing 2. If an error occurred, it gets stored in mErrorMsgString variable and shown to the end-users. It’s a very good programming practice to provide error handling capability in all your application code. Many published VB.NET papers do not provide this important implementation code today.
Private Sub MySQLComboBoxForm_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs)
Handles MyBase.Load
Cursor = Cursors.WaitCursor
Try
Using LoopUpObject As New LoopUpClass(mMySQLConnectionString)
Call LoopUpObject.LoopUpDataLoad(StatesComboBox, _
"usp_states_select_postal_name", _
mErrorMsgString)
If Not IsNothing(mErrorMsgString) Then
Cursor = Cursors.Default
MessageBox.Show(mErrorMsgString, _
Me.Text, _
MessageBoxButtons.OK, _
MessageBoxIcon.Error)
End If
Call LoopUpObject.LoopUpDataLoad(CapitalsComboBox, _
"usp_states_select_postal_capital", _
mErrorMsgString)
If Not IsNothing(mErrorMsgString) Then
Cursor = Cursors.Default
MessageBox.Show(mErrorMsgString, _
Me.Text, _
MessageBoxButtons.OK, _
MessageBoxIcon.Error)
End If
End Using
Catch exError As Exception
MessageBox.Show(exError.Message)
End Try
Cursor = Cursors.Default
End Sub
Listing 4: Load event of the form “Load ComboBox with MySQL”
The user stored procedures `usp_states_select_postal_name` and `usp_states_select_postal_capital` are passed by value and shown in Listing 5. Both procedures were developed based on the USA state table (`state`) with data (Listing 6).
DROP PROCEDURE IF EXISTS `usp_states_select_postal_name`;
CREATE PROCEDURE `usp_states_select_postal_name`( )
BEGIN
SELECT `states`.`postal`, `states`.`statename`
FROM `states`
ORDER BY `states`.`postal`;
END;
DROP PROCEDURE IF EXISTS `usp_states_select_postal_capital `;
CREATE PROCEDURE `usp_states_select_postal_capital`( )
BEGIN
SELECT `states`.`postal`, `states`.`capital`
FROM `states`
ORDER BY `states`.`capital`;
END;
Listing 5: User stored procedures to select USA States and Capitals
DROP TABLE IF EXISTS `states`;
CREATE TABLE `states` (
`statename` varchar(20) NOT NULL,
`abbrev` varchar(10) NOT NULL,
`postal` char(2) NOT NULL,
`capital` varchar(20) NOT NULL,
PRIMARY KEY (`postal`),
KEY `statename` (`statename`),
KEY `capital` (`capital`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Alaska','Alaska','AK','Juneau');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Alabama','Ala.','AL','Montgomery');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Arkansas','Ark.','AR','Little Rock');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Arizona','Ariz.','AZ','Phoenix');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('California','Calif.','CA','Sacramento');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Colorado','Colo.','CO','Denver');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Connecticut','Conn.','CT','Hartford');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Dist. of Columbia','D.C.','DC','Washington');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Delaware','Del.','DE','Dover');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Florida','Fla.','FL','Tallahassee');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Georgia','Ga.','GA','Atlanta');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Guam','Guam','GU','Agaña');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Hawaii','Hawaii','HI','Honolulu');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Iowa','Iowa','IA','Des Moines');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Idaho','Idaho','ID','Boise');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Illinois','Ill.','IL','Springfield');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Indiana','Ind.','IN','Indianapolis');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Kansas','Kans.','KS','Topeka');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Kentucky','Ky.','KY','Frankfort');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Louisiana','La.','LA','Baton Rouge');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Massachusetts','Mass.','MA','Boston');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Maryland','Md.','MD','Annapolis');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Maine','Maine','ME','Augusta');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Michigan','Mich.','MI','Lansing');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Minnesota','Minn.','MN','St Paul');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Missouri','Mo.','MO','Jefferson City');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Mississippi','Miss.','MS','Jackson');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Montana','Mont.','MT','Helena');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('North Carolina','N.C.','NC','Raleigh Durham');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('North Dakota','N.D.','ND','Bismarck');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Nebraska','Nebr.','NE','Lincoln');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('New Hampshire','N.H.','NH','Concord');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('New Jersey','N.J.','NJ','Trenton');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('New Mexico','N.M.','NM','Santa Fe');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Nevada','Nev.','NV','Carson City');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('New York','N.Y.','NY','Albany');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Ohio','Ohio','OH','Columbus');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Oklahoma','Okla.','OK','Oklahoma City');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Oregon','Ore.','OR','Salem');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Pennsylvania','Pa.','PA','Harrisburg');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Puerto Rico','P.R.','PR','San Juan');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Rhode Island','R.I.','RI','Providence');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('South Carolina','S.C.','SC','Columbia');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('South Dakota','S.D.','SD','Pierre');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Tennessee','Tenn.','TN','Nashville');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Texas','Tex.','TX','Austin');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Utah','Utah','UT','Salt Lake City');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Virginia','Va.','VA','Richmond');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Virgin Islands','V.I.','VI','Charlotte Amalie');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Vermont','Vt.','VT','Montpelier');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Washington','Wash.','WA','Olympia');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Wisconsin','Wis.','WI','Madison');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('West Virginia','W.Va.','WV','Charleston');
insert into `states`(`statename`,`abbrev`,`postal`,`capital`) values
('Wyoming','Wyo.','WY','Cheyenne');
Listing 6: State table definition and SQL insert data statements
After the ComboBoxes are loaded with data, the users can choice any items (States and Capitals) by clicking and selecting on them. The selected ID and Name values must be known by the program for any possible selection. In general this ID represents a foreign key in the master table to keep data integrity, inserting and updating records. The Name values are showing to the user and some times it requires data validation depending on application business rules. In the last couple of years I have gotten a lot of requests from many users to load the combination of the ID and Name ([ID] – [Name]) as a general shown Name field in the ComboBox, ListBox and/or CheckListBox read-only controls. It seems to me that this combination sometimes gives the end-users more business meaning about the select data than a single Name only. I guess they may start understanding the purpose of the ID value as a primary key of a table in business applications development. Let’s look at the code (Listing 7) of the Postal Code button in Figure 1. This code is calling the LoopUpGetIDString() function (Listing 8) to retrieve the value of the postal code by the selected state. For example, for the ‘Alaska’ state we got ‘AK’ postal code. If an error does not occur the postal code is shown to the user by using the Show() method of the MessageBox class object.
Private Sub PostalCodeButton1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs)
Handles PostalCodeButton1.Click
Cursor = Cursors.WaitCursor
Dim PostalCodeString As String
Using LoopUpObject As New LoopUpClass()
PostalCodeString = LoopUpObject.LoopUpGetIDString(StatesComboBox, _
mErrorMsgString)
If Not IsNothing(mErrorMsgString) Then
Cursor = Cursors.Default
MessageBox.Show(mErrorMsgString, _
Me.Text, _
MessageBoxButtons.OK, _
MessageBoxIcon.Error)
Else
Cursor = Cursors.Default
MessageBox.Show("Postal Code: " & PostalCodeString, _
Me.Text, _
MessageBoxButtons.OK, _
MessageBoxIcon.Information)
End If
End Using
Cursor = Cursors.Default
End Sub
Listing 7: Click event of the Postal Code button in Figure 1
The LoopUpGetIDString() and LoopUpGetIDInt32() functions (Listing 8) return the ID value for a character and a numeric data type respectively. The LoopUpGetNameString() function (Listing 8) returns the Name as a character data type only. These functions use the SelectedIndex() property to position the item number and the CType() explicit conversion function converts the selected ComboBox item object into ListItemClass class (Listing 3). Using the read-only properties FieldIDString, FieldIDInt32 and FieldName (Listing 3) the ID and the Name values can be retrieved.
Public Function LoopUpGetIDString(ByVal pComboBox As ComboBox, _
ByRef pErrorMsgString As String) As String
Dim IndexNumberInt32 As Int32, GetIDString As String
Try
IndexNumberInt32 = pComboBox.SelectedIndex()
GetIDString = CType(pComboBox.Items(IndexNumberInt32), ListItemClass).FieldIDString
Return (GetIDString)
Catch exErr As Exception
Return (Nothing)
pErrorMsgString = exErr.Message
End Try
End Function
Public Function LoopUpGetIDInt32(ByVal pComboBox As ComboBox, _
ByRef pErrorMsgString As String) As Int32
Dim IndexNumberInt32 As Int32, GetIDInt32 As Int32
Try
IndexNumberInt32 = pComboBox.SelectedIndex()
GetIDInt32 = CType(pComboBox.Items(IndexNumberInt32), ListItemClass).FieldIDInt32
Return (GetIDInt32)
Catch exErr As Exception
pErrorMsgString = exErr.Message
Return (Nothing)
End Try
End Function
Public Function LoopUpGetNameString(ByVal pComboBox As ComboBox, _
ByRef pErrorMsgString As String) As String
Dim IndexNumberInt32 As Int32, GetNameString As String
Try
IndexNumberInt32 = pComboBox.SelectedIndex()
GetNameString = CType(pComboBox.Items(IndexNumberInt32), ListItemClass).FieldName
Return (GetNameString)
Catch exErr As Exception
pErrorMsgString = exErr.Message
Return (Nothing)
End Try
End Function
Listing 8: Lookup ID functions for a character and a numeric data type
As I explained above, the ListItemClass generic class (Listing 3) was created to store and read-only the values of the ID and Name columns from the lookup tables. In business applications with lookup tables in the database server finding the ID and Name values is a must. To find a string Name item, for example, in a ComboBox, the FindString() method is the easiest way to do so. With two lines of code (Listing 9) the string Name can be found by returning the Index number from the FindString() method and the Index position by using the SelectedIndex() property.
Public Sub LoopUpFindNameString(ByVal pComboBox As ComboBox, _
ByVal pFindNameString As String, _
ByRef pErrorMsgString As String)
Dim IndexNumberInt32 As Int32
Try
IndexNumberInt32 = pComboBox.FindString(pFindNameString)
pComboBox.SelectedIndex() = IndexNumberInt32
Catch exErr As Exception
pErrorMsgString = exErr.Message
End Try
End Sub
Listing 9: Lookup find string Name subroutine
Finding the ID value in a ComboBox requires more codes. I did not find any ID method implemented in the ComboBox control. In this particular case we need to loop through the entire Item collection and compare two string values as shown in Listing 10. The Compare() method of the String class object compares two strings and returns a Int32 zero value if both strings are equal. If the strings are equal the SelectedIndex() property position the Index in the ComboBox as I explained before. If the strings are not equal, the ComboBox does not show any data by setting SelectedIndex() equal -1.
Public Sub LoopUpFindIDString(ByVal pComboBox As ComboBox, _
ByVal pFindIDString As String, _
ByRef pErrorMsgString As String)
Dim LoopInt32, CompareInt32 As Int32
Dim GetIDString As String
Dim IsFoundBoolean As Boolean = False
Try
For LoopInt32 = 0 To pComboBox.Items.Count - 1
GetIDString = CType(pComboBox.Items(LoopInt32), ListItemClass).FieldIDString
CompareInt32 = String.Compare(GetIDString, pFindIDString)
If CompareInt32 = 0 Then
pComboBox.SelectedIndex() = LoopInt32
IsFoundBoolean = True
Exit For
End If
Next LoopInt32
If Not IsFoundBoolean Then
pComboBox.SelectedIndex() = -1
End If
Catch exErr As Exception
pErrorMsgString = exErr.Message
End Try
End Sub
Listing 10: Lookup find string ID subroutine
When the user selects an item in the ComboBox the ID is determined by using any of the functions in Listing 8 as we discussed before (LoopUpGetIDString() and LoopUpGetIDInt32()). Often, based on the selected ID, we need to find and load with data another control from different table. In my solution project example (Figure 1), when the user select a State the Capital TextBox control is loaded data. As you can see for ‘Alaska’ state the capital is ‘Juneau’, and for the capital ‘Agaña’ the state is ‘Guam’. In both case the Postal Code represents the selected ID as the primary key in `state` table (Listing 6). How to implement this approach in Windows environment? One of the easiest and fastest ways in MySQL 5.0/VB.NET 2005 is by using the SelectedIndexChanged() event of the ComboBox and MySQL stored procedures. I remember in the old days of VB 6.0 we used to use the Click() event and dynamic SQL statements with MDAC technology. Listing 11 and 12 shows the SelectedIndexChanged() events of the Sates and Capitals ComboBoxes. As you can see, in both events, the Postal Code ID (PostalCodeString) is determined first by using the LoopUpGetIDString() function.
Private Sub StatesComboBox_SelectedIndexChanged(ByVal sender As System.Object. _
ByVal e As System.EventArgs)
Handles StatesComboBox.SelectedIndexChanged
Dim PostalCodeString As String
Dim CapitalNameString As String = Nothing
Using LoopUpObject As New LoopUpClass(mMySQLConnectionString)
PostalCodeString = LoopUpObject.LoopUpGetIDString(StatesComboBox, _
mErrorMsgString)
Call LoopUpObject.LoopUpGetCapitalName(PostalCodeString, _
"usp_states_capital_by_postal", _
CapitalNameString, _
mErrorMsgString)
CapitalTextBox.Text = CapitalNameString
End Using
End Sub
Listing 11: SelectedIndexChanged() event of the States ComboBox
Private Sub CapitalsComboBox_SelectedIndexChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs)
Handles CapitalsComboBox.SelectedIndexChanged
Dim PostalCodeString As String
Dim StateNameString As String = Nothing
Using LoopUpObject As New LoopUpClass(mMySQLConnectionString)
PostalCodeString = LoopUpObject.LoopUpGetIDString(CapitalsComboBox,
mErrorMsgString)
Call LoopUpObject.LoopUpGetStateName(PostalCodeString, _
"usp_states_name_by_postal", _
StateNameString, _
mErrorMsgString)
StateTextBox.Text = StateNameString
End Using
End Sub
Listing 12: SelectedIndexChanged() event of the Capitals ComboBox
The user stored procedures shown in Listing 13 (`usp_states_capital_by_postal` and `usp_states_name_by_postal`) have Postal Code as input parameter (par_postalcode) and Capital and State (par_capital and par_statename) as output parameters respectively. Both procedures were developed and tested using Toad for MySQL 2.0.3 freeware version from Quest Software, Inc.
DROP PROCEDURE IF EXISTS `usp_states_capital_by_postal`;
CREATE PROCEDURE `usp_states_capital_by_postal`(
IN par_postalcode CHAR(2),
OUT par_capital VARCHAR(20)
)
BEGIN
SELECT `states`.`capital` INTO par_capital
FROM `states`
WHERE `postal` = par_postalcode;
END;
DROP PROCEDURE IF EXISTS `usp_states_name_by_postal`;
CREATE PROCEDURE `usp_states_name_by_postal`(
IN par_postalcode CHAR(2),
OUT par_statename VARCHAR(20)
)
BEGIN
SELECT `states`.`statename` INTO par_statename
FROM `states`
WHERE `postal` = par_postalcode;
END;
Listing 13: User stored procedures to retrieve State and Capital name by Postal Code
The subroutines LoopUpGetCapitalName() and LoopUpGetStateName() are shown in Listing 14 and 15 respectively.
Public Sub LoopUpGetCapitalName(ByVal pPostalCodeString As String, _
ByVal pStoredProcedureString As String, _
ByRef pCapitalNameString As String, _
ByRef pErrorMsgString As String)
Dim MySqlParameterPostal As New MySqlParameter
Dim MySqlParameterCapital As New MySqlParameter
Try
Using mMySqlConnection As New MySqlConnection(mMySQLConnectionString)
mMySqlConnection.Open()
Using mMySqlCommand As New MySqlCommand
With mMySqlCommand
.Connection = mMySqlConnection
.CommandType = CommandType.StoredProcedure
.CommandText = pStoredProcedureString
With MySqlParameterPostal
.ParameterName = "?par_postalcode"
.Direction = ParameterDirection.Input
.MySqlDbType = MySqlDbType.VarChar
.Size = 2
.Value = pPostalCodeString
End With
.Parameters.Add(MySqlParameterPostal)
With MySqlParameterCapital
.ParameterName = "?par_capital"
.Direction = ParameterDirection.Output
.MySqlDbType = MySqlDbType.VarChar
.Size = 20
.Value = pCapitalNameString
End With
.Parameters.Add(MySqlParameterCapital)
.ExecuteNonQuery()
mObjectjValue = .Parameters("?par_capital").Value
If Not IsDBNull(mObjectjValue) Then
pCapitalNameString = mObjectjValue.ToString
Else
pCapitalNameString = String.Empty
End If
End With
End Using
End Using
Catch exErr As Exception
pErrorMsgString = exErr.Message
Finally
If Not IsNothing(MySqlParameterPostal) Then
MySqlParameterPostal = Nothing
End If
If Not IsNothing(MySqlParameterCapital) Then
MySqlParameterCapital = Nothing
End If
End Try
End Sub
Figure 14: Retrieve Capital name subroutine by Postal Code
Public Sub LoopUpGetStateName(ByVal pPostalCodeString As String, _
ByVal pStoredProcedureString As String, _
ByRef pStateNameString As String, _
ByRef pErrorMsgString As String)
Dim MySqlParameterPostal As New MySqlParameter
Dim MySqlParameterState As New MySqlParameter
Try
Using mMySqlConnection As New MySqlConnection(mMySQLConnectionString)
mMySqlConnection.Open()
Using mMySqlCommand As New MySqlCommand
With mMySqlCommand
.Connection = mMySqlConnection
.CommandType = CommandType.StoredProcedure
.CommandText = pStoredProcedureString
With MySqlParameterPostal
.ParameterName = "?par_postalcode"
.Direction = ParameterDirection.Input
.MySqlDbType = MySqlDbType.VarChar
.Size = 2
.Value = pPostalCodeString
End With
.Parameters.Add(MySqlParameterPostal)
With MySqlParameterState
.ParameterName = "?par_statename"
.Direction = ParameterDirection.Output
.MySqlDbType = MySqlDbType.VarChar
.Size = 20
.Value = pStateNameString
End With
.Parameters.Add(MySqlParameterState)
.ExecuteNonQuery()
mObjectjValue = .Parameters("?par_statename").Value
If Not IsDBNull(mObjectjValue) Then
pStateNameString = mObjectjValue.ToString
Else
pStateNameString = String.Empty
End If
End With
End Using
End Using
Catch exErr As Exception
pErrorMsgString = exErr.Message
Finally
If Not IsNothing(MySqlParameterPostal) Then
MySqlParameterPostal = Nothing
End If
If Not IsNothing(MySqlParameterState) Then
MySqlParameterState = Nothing
End If
End Try
End Sub
Figure 15: Retrieve State name subroutine by Postal Code
After the article ?Define and Store MySQL ADO Connection String in VB.NET 2005” was published online, I got many questions from many Open Source application developers around the world. One of the main questions was how to execute a MySQL 5.0 stored procedure in VB.NET 2005 with input and output parameters. They were looking for a simple code implementation and a good explanation using the latest Connector/NET 5.0.6. In quality of example, let’s look carefully at the subroutine LoopUpGetCapitalName() in Listing 14. Looking for a better explanation, I have decided to divide this code in five main blocks from Listing 14.1 to Listing 14.5. The first Listing 14.1 uses the Using statement to create the Connection object mMySqlConnection and initialize it. The variable mMySQLConnectionString represents the MySQL Connection String defined in the app.config file as Server=xxx;Database=xxx;Uid=xxx;Pwd=xxx. After the connection is open with the Open() method the Command object mMySqlCommand is created and initialized with the Using statement. Three main properties of the Command object needs to be determined: 1.Connection property sets to Connection object, 2. CommandType property sets to stored procedure type (CommandType.StoredProcedure) and 3. CommandText property sets to the stored procedure object name developed in the MySQL database server.
Using mMySqlConnection As New MySqlConnection(mMySQLConnectionString)
mMySqlConnection.Open()
Using mMySqlCommand As New MySqlCommand
With mMySqlCommand
.Connection = mMySqlConnection
.CommandType = CommandType.StoredProcedure
.CommandText = pStoredProcedureString
End With
End Using
End Using
Listing 14.1
Because the user stored procedure `usp_states_capital_by_postal` (Listing 13) has two parameters we need to create, initialize, set and add these parameters to the Command object parameters collection. Listing 14.2 and 14.3 shows the code for Postal Code (MySqlParameterPostal) and Capital Name (MySqlParameterCapital) MySQL parameters. As you can see, for the Connector/NET 5.0.6, the question sign (?) should be included before the parameter name. The Postal Code parameter direction is setup to Input and the Capital Name is setup to Output as required from the user stored procedure `usp_states_capital_by_postal` definition.
Dim MySqlParameterPostal As New MySqlParameter
With MySqlParameterPostal
.ParameterName = "?par_postalcode"
.Direction = ParameterDirection.Input
.MySqlDbType = MySqlDbType.VarChar
.Size = 2
.Value = PostalCodeString
End With
.Parameters.Add(MySqlParameterPostal)
Listing 14.2
Dim MySqlParameterCapital As New MySqlParameter
With MySqlParameterCapital
.ParameterName = "?par_capital"
.Direction = ParameterDirection.Output
.MySqlDbType = MySqlDbType.VarChar
.Size = 20
.Value = pCapitalNameString
End With
.Parameters.Add(MySqlParameterCapital)
Listing 14.3
After the Command object is executed with ExecuteNonQuery() method the output parameter value Capital Name can be retrieved as shown in Listing 14.4. The object variable mObjectjValue stores the Capital Name and it needs to be checked for Null database value. To do that the IsDBNull() function was used. If the Capital Name value is not Null, it’s gets stored in the pCapitalNameString function by reference parameter. On the other hand, if the value is Null the pCapitalNameString is set to empty string (String.Empty) using the String class object.
.ExecuteNonQuery()
mObjectjValue = .Parameters("?par_capital").Value
If Not IsDBNull(mObjectjValue) Then
pCapitalNameString = mObjectjValue.ToString
Else
pCapitalNameString = String.Empty
End If
Listing 14.4
After everything is done we need to release .NET used unmanaged resource as required. The Using statement takes care of releasing the resources for the mMySqlConnection Connection object and mMySqlCommand command object. The MySQL parameter objects MySqlParameterPostal and MySqlParameterCapital needs to be destroyed by setting them to Nothing in the Finally block (Listing 14.5). Setting to Nothing these objects will enable the Garbage Collection (GC) to release them.
Finally
If Not IsNothing(MySqlParameterPostal) Then
MySqlParameterPostal = Nothing
End If
If Not IsNothing(MySqlParameterCapital) Then
MySqlParameterCapital = Nothing
End If
End Try
Listing 14.5
From this paper you can find the following main conclusions:
To download the source codes and a PDF format for this article go to Visual
WWW Downloads.
Read and post comments on this article in the MySQL Forums. There are currently 1 comments.
