ARTICLE

An Approach to Viewing the Structure of a Database Using VB 2005

Posted by Scott Lysle Articles | ADO.NET in VB.NET September 04, 2006
This article describes an easy approach to examining all of the tables, views, and columns in a database.
Download Files:
 
Reader Level:

Introduction:

This article describes an easy approach to examining all of the tables, views, and columns in a database, the article is written to describe an example application that may be used to connect to a database (Oracle, SQL Server, or MS Access), view that database's tables and views, and drill down into each table or view to generate a list of all of its contained columns.  Further, the application will allow the user to examine the definition associated with each column (e.g., its data type, caption, default value, etc.).

The application does not serve any particular purpose and has only a few key methods associated with it.  Whilst the application does not perform any sort of useful task, the application could be used to form the basis for some useful tool, such as one that would map the fields in one database table to the fields in another database table, or it could be used as the basis for a tool that allowed a user to formulate an ad hoc query.

Figure 1:  The Demonstration Application Running

Getting Started:

In order to get started, unzip the included project and open the solution in the Visual Studio 2005 environment.   In the solution explorer, you should note three significant files:

  • AppVars.vb:  Containing some application wide variables contained in a module.
  • frmMain.vb:  Containing the main application and most of the code.
  • frmConnect.vb:  Containing a dialog used to connect to a database.

Application Variables.

Let's start off with the first of the project files mentioned, AppVars.vb.  Open the file to view it in the IDE.  This is simply a code module and it does contain any methods at all, only a collection of variables shared between frmConnect.vb and frmMain.vb.  The entire body of code is as follows:

Module AppVars

 

    Public gCurrentDataModel As String     'current data model selected

    Public gCurrentDatabaseType As String  'type of database used

    Public gConnectionString As String     'full connect string

    Public gInitialCatalog As String       'sql server initial catalog for

    Public gProviderString As String       'provider name used for access

    Public gServerName As String       'server name for live data access

    Public gUserID As String           'its the user id used to connect

    Public gPassword As String         'its the password value used

    Public gCurrentTables() As String  'tables stored to populate lists

    Public gCurrentViews() As String   'views stored to populate lists

 

End Module

The first variable defined is used to contain the name of the data model, this is arbitrary and serves no purpose within this application, it is merely a label used to identify the connection type.

The next variable is used to contain the database type, such MS Access or SQL Server.

The connection string variable, as you might have guessed, holds the connection string.

The initial catalog variable is used only in SQL Server connections and it contains, yep, the initial catalog name.

The provider string contains the name of the data provider.

User ID and Password are pretty self-explanatory; in the real world you may wish to encrypt the password value, in fact you may wish to store all of these items  within the application as resources or in some other format rather than to place them into a module file; but for the sake of this example, the module will do nicely.

The current tables and current views are string arrays used to capture all of the tables and views associated with a connection.  This has been added in the event that one may need to persist these items and make them more quickly available to controls within some more useful application.  I do populate these string arrays but I don't have any real use for them in this demonstration application.

Whilst this application does use the module with these variables, a better approach may be to create a  set of serializable classes that contains properties for each type of database, and instance, populate, and serialize the correct class type given the current connection.

Connection Dialog.

The connection dialog is contained in frmConnect.vb; this dialog is used to capture the variables necessary to create a viable connection to an Oracle, SQL Server, or MS Access database.  The dialog contains a tabbed pane with three panels, one for each connection type.  Each panel contains all of the controls necessary to generate a connection.  The user may test the connections from this dialog, and once the user accepts the dialog, the connection information will be persisted and made available to the application.

Figure 2:  Connection Dialog with SQL Server Options Displayed

The code is pretty simple, if you'd care to open the code view up in the IDE you will see that the code file begins like this:

Imports System.Collections

Imports System.IO

Imports System.Runtime.Serialization.Formatters.Binary

Imports System.Runtime.Serialization 

 

Public Class frmConnect

 

#Region "Declarations"

 

    Private mCurrentDataModel As String

    Private mCurrentDatabaseType As String

    Private mConnectionString As String

    Private mProviderString As String

    Private mServerName As String

    Private mInitialCatalog As String

    Private mServerPort As String

    Private mDatabaseName As String

    Private mUserID As String

    Private mPassword As String

 

#End Region

Note that the imports include both serialization and IO; these are used to persist the connection information.  In the declarations region, note that local copies of the variables used in the AppVars.vb file are declared (except for the view and table arrays).  These are the member variables that will be used within this class.

For each of the OK buttons (one for each database type supported), there will be a handler written that will basically conform to the following code block:

Private Sub btnOracleOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOracleOK.Click

 

        mCurrentDataModel = "MyOracle"

        mCurrentDatabaseType = "Oracle"

 

        mProviderString = txtOracleProvider.Text

        mPassword = txtOraclePassword.Text

        mUserID = txtOracleUserID.Text

        mServerName = txtOracleDBname.Text

 

        mConnectionString = "Provider=" & mProviderString & _

                            ";Password=" & mPassword & _

                            ";User ID=" & mUserID & _

                            ";Data Source=" & mServerName

 

        'Test Connection

        Dim cn As ADODB.Connection

        cn = New ADODB.Connection

        cn.CursorLocation = ADODB.CursorLocationEnum.adUseClient

 

        Try

 

            cn.Open(mConnectionString)

            cn.Close()

            cn = Nothing

 

        Catch ex As Exception

 

            MessageBox.Show(ex.Message.ToString(), "Application Settings

            Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

 

        End Try

 

        cn = Nothing

 

        'set app setting global variables

        AppVars.gCurrentDataModel = mCurrentDataModel

        AppVars.gCurrentDatabaseType = mCurrentDatabaseType

        AppVars.gConnectionString = mConnectionString

        AppVars.gProviderString = mProviderString

        AppVars.gServerName = mServerName

        AppVars.gUserID = mUserID

        AppVars.gPassword = mPassword

 

        'create hashtable to hold settings

        Dim ht As New Hashtable

        ht.Add("CurrentDataModel", gCurrentDataModel)

        ht.Add("CurrentDatabaseType", gCurrentDatabaseType)

        ht.Add("ConnectionString", gConnectionString)

        ht.Add("ProviderString", gProviderString)

        ht.Add("ServerName", gServerName)

        ht.Add("UserID", gUserID)

        ht.Add("Password", gPassword)

 

        'serialize data

        SerializeAppVars(ht)

 

        'cache view names

        StoreViewNames()

 

        Me.Dispose() 


End Sub

This handler begins by capturing the user input from the dialog  and the local variables are set to the form values; I did not do it in the example but you should validate all of these inputs.  Next, the handler will attempt to use the values to create a connection object and it will try to open this connection to confirm that it works.   If the attempt fails, the user will be alerted, if it passes, the values will be added to a hash table and the hash table will be serialized and persisted into a file in the application's path; this occurs in the SerializeAppVars() call.  After the serialization occurs, the view and table names will be stored by the StoreViewNames() call, and lastly, the form will be disposed of by the Me. Dispose call.

Each of the three database tabs has an OK button and each of those buttons is handled in a manner consistent with this approach.  Examine each specific handler to observe the minor differences related to that particular tab's variables.
The StoreViewNames () subroutine is used to persist the captured values so that they may be recovered the next time the application is started.  This code is a little more interesting:

Public Sub StoreViewNames()

 

        Dim SchemaTable As DataTable

        Dim arrViews() As String = Nothing

        Dim arrTables() As String = Nothing

 

        If AppVars.gConnectionString <> "" Then

 

            'Connect to the database

            Dim conn As New System.Data.OleDb.

            OleDbConnection(AppVars.gConnectionString)

 

            Try

                conn.Open()

 

                'get view names

                SchemaTable =

                conn.GetOleDbSchemaTable(System.Data.

                OleDb.OleDbSchemaGuid.Tables, New Object()

                {Nothing, Nothing, Nothing, Nothing})

               

                Dim int As Integer

                Dim incr As Integer = 0

 

                For int = 0 To SchemaTable.Rows.Count - 1

 

                    If SchemaTable.Rows(int)!TABLE_TYPE.ToString = "TABLE"

                    Then

 

                        Dim strTempTableName As String =

                        SchemaTable.Rows(int)!TABLE_NAME.ToString()

                        ReDim Preserve arrTables(incr + 1)

                        arrTables(incr) = strTempTableName

                        incr = incr + 1

 

                    End If

 

                Next

 

                For int = 0 To SchemaTable.Rows.Count - 1

 

                    If SchemaTable.Rows(int)!TABLE_TYPE.ToString = "VIEW"

                    Then

 

                        Dim strTempViewName As String =

                        SchemaTable.Rows(int)!TABLE_NAME.ToString()

                        ReDim Preserve arrViews(incr + 1)

                        arrViews(incr) = strTempViewName

                        incr = incr + 1

 

                    End If

 

                Next

 

            Catch ex As Exception

 

                MessageBox.Show(ex.Message.ToString(), "Application Settings

                Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

 

            Finally

 

                conn.Close()

                conn = Nothing

 

            End Try

 

            AppVars.gCurrentViews = arrViews

            AppVars.gCurrentTables = arrTables

 

        End If 

End Sub

In this bit of code, note in the declarations that a data table is declared and named "SchemaTable"; after establishing an OleDbConnection, note the bit of code that assigns that table to hold the connection's schema table; it is this table that makes it easy to examine the structure of the database and its contents.   After the schema table is set, the rest of the code loops through the table's rows and finds first the tables and next the views.  Found tables are added to the string array containing the table names and found views are added to the string array containing the view names.

Once the arrays are set, the subroutine closes by passing the two arrays to the AppVars current views and current tables variables.

The SerializeAppVars () subroutine is used to persist the variables into a file stored in the application's path; this file is used to permit the application to open with the last established connection already in place.  The code used to serialize the values is as follows:

Private Sub SerializeAppVars(ByVal ht As Hashtable)

 

        Dim strPath As String

        strPath = Application.StartupPath & "\appset.con"

 

        Dim fs As New FileStream(strPath, FileMode.OpenOrCreate)

        Dim formatter As New BinaryFormatter

 

        Try

 

            formatter.Serialize(fs, ht)

            fs.Close()

 

        Catch ex As SerializationException

 

            MessageBox.Show(ex.Message, "Application Settings Serialization

            Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

 

        End Try 

End Sub

As you can see, this is a pretty simple subroutine.  It accepts the hash table generated in the OK button event handler, opens a file stream to a file called "appset.con" which is placed in the application startup path, a binary formatter is used to serialize the hash table and store it in the file.

Each of the dialog's three tabs also contains a test button.  As with the OK button, the functionality is virtually the same for all three test buttons and so I will only show one in this document.  Review the example project to see the differences between the three handlers but note that basically only the variables and the format of the connections string vary from button to button. The Oracle test button's handler looks like this:

Private Sub btnOracleTest_Click(ByVal sender As System.Object, ByVal e As

    System.EventArgs) Handles btnOracleTest.Click

 

    mProviderString = txtOracleProvider.Text

    mPassword = txtOraclePassword.Text

    mUserID = txtOracleUserID.Text

    mServerName = txtOracleDBname.Text

 

    mConnectionString = "Provider=" & mProviderString & _

                            ";Password=" & mPassword & _

                            ";User ID=" & mUserID & _

                            ";Data Source=" & mServerName

 

    'Test Connection

    Dim cn As ADODB.Connection

    cn = New ADODB.Connection

    cn.CursorLocation = ADODB.CursorLocationEnum.adUseClient

 

    Try

        cn.Open(mConnectionString)

        cn.Close()

        cn = Nothing

        MessageBox.Show("Connection attempt successful, the database

        connection information provided has been successfully used to

        connect to the database.", "Connection Successful",

        MessageBoxButtons.OK, MessageBoxIcon.Information)

 

    Catch ex As Exception

 

        MessageBox.Show("Connection attempt failed.", "Unable to

            Connect", MessageBoxButtons.OK, MessageBoxIcon.Error)

 

    End Try

 

    cn = Nothing


End
Sub

Oddly enough, I used an ADO connection to perform the test, you may replace this with an OleDbConnection object and perform the same test.  The code is straight forward, you will note that the form variables are collected and passed to the local variables.  These variables are used to format a connection string, the connection string is opened as a test, if it opens, the test passes, if it does not open, the test fails.  The user is informed of either outcome.

The rest of the code in the connection dialog is pretty standard and is used to do things like close the form or browse for a file.  Review the contents of the class in the IDE to review the rest of the content; there is sufficient commenting applied to make it self-explanatory.

The Main Form.

The main application is contained in the frmMain.vb class.  This form is used to gain access to the connection dialog, and to load the database information into the form's controls.  The form is structured with a menu at the top, this menu contains the options to exit the application, to create a new connection, to view the current connection, and to load the schema information for the current database associated with the connection.  In the main area of the form, there are three group boxes, one contains a list box control used to display the tables contained in the current database, one contains a list box control used to display the views contained in the current database, and one contains a list box used to display the columns contained in any view or table selected from the table or view list box controls.

Figure 3:  The Main Form with an SQL Server Connection Active

The class starts out with the following code:

Imports System.Data

Imports System.Data.OleDb

Imports System.Data.SqlClient

Imports System.Data.SqlTypes

Imports System.Collections

Imports System.Text

Imports System.IO

Imports System.Runtime.Serialization.Formatters.Binary

Imports System.Runtime.Serialization 

 

Public Class frmMain 

 

#Region "Declarations"

 

    Public SelectedTable As String

    Private mConnectionString As String

    Private mTableSelected As Boolean 

#End Region

In the imports section, note the data and data related class library additions; you may need to open the project references and add any missing references to the project if any of these imports is not supported when you load the application into the IDE.  Following the imports, a declarations region was added and three variables declared.  These variables are subsequently used within this class.

Figure 4:  Demonstration Project References

The next section of code worth looking at is the form load event handler, it looks like this:

Private Sub frmMain_Load(ByVal sender As Object, ByVal e As

    System.EventArgs) Handles Me.Load

 

    Dim mConnectionString As String = ""

    DeserializeAppSettings()

 

    If AppVars.gConnectionString <> "" Then

        mConnectionString = AppVars.gConnectionString.ToString()

        Me.Text = "Database - " & AppVars.gCurrentDatabaseType.ToString

    Else

        Exit Sub

    End If

 

    If Not Trim(mConnectionString) = Nothing Then

         Try

            OpenTablesOnStart(Trim(mConnectionString))

         Catch ex As Exception

            MessageBox.Show(ex.Message.ToString(), "Data Load Error",

            MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

 

        End Try

 

    End If


End
Sub

This event handler is simple enough, if an existing connection file exists it is opened and deserialized.  The form label is updated to show the current database type and if the connection string exists, it is passed to a subroutine called OpenTablesOnStart which subsequently loads the tables and views associated with the current connection into the table and view list box controls.

The OpenTablesOnStart subroutine called by the form load event handler is used to populate the table and view list boxes, its content looks like this:

Private Sub OpenTablesOnStart(ByVal strConn As String)

 

    Dim SchemaTable As DataTable

 

    'Connect to the database

    Dim conn As New System.Data.OleDb.OleDbConnection(strConn)

 

    Try

        conn.Open()

        'Get table and view names

        SchemaTable = conn.GetOleDbSchemaTable(System.Data.

  OleDb.OleDbSchemaGuid.Tables, New Object()

  {Nothing, Nothing, Nothing, Nothing})

 

        lstTables.Items.Clear()

        lstViews.Items.Clear()

 

        Dim int As Integer

        For int = 0 To SchemaTable.Rows.Count - 1

            If SchemaTable.Rows(int)!TABLE_TYPE.ToString = "TABLE" Then

               lstTables.Items.Add(SchemaTable.Rows(int)!TABLE_NAME.ToString())

            End If

        Next

 

        For int = 0 To SchemaTable.Rows.Count - 1

             If SchemaTable.Rows(int)!TABLE_TYPE.ToString = "VIEW" Then          

                lstViews.Items.Add(SchemaTable.Rows(int)!TABLE_NAME.ToString())

             End If

        Next

 

    Catch ex As Exception

        MessageBox.Show(ex.Message.ToString(), "Data Load Error",

        MessageBoxButtons.OK, MessageBoxIcon.Exclamation)

    End Try

    conn.Close()

End Sub

A quick scan of the subroutine will reveal that is operates in a manner consistent with the subroutine used in the frmConnect.vb class to populate the view and table arrays.  Again, a data table variable is declared and it is set to contain the schema for the target database.  This table is then looped twice, once to capture the tables and once to capture the views.  The views and tables collected are dropped into the list boxes on the main form.

Each of the list boxes containing the views and the tables has a handler used to recover the columns from that view or table and to display the column collection in the fields list box.  Both handlers operate in basically the same way, here is an example showing the table list's handler:

Private Sub lstTables_SelectedIndexChanged(ByVal sender As System.Object,

    ByVal e As System.EventArgs) Handles lstTables.SelectedIndexChanged

 

    mTableSelected = True

 

    Dim tblName As String

    tblName = lstTables.SelectedItem.ToString()

 

    Dim conn As New System.Data.OleDb.OleDbConnection(mConnectionString)

 

    Try

        conn.Open()

        lstFields.Items.Clear()

 

        Dim dt_field As System.Data.DataTable = _

                    conn.GetOleDbSchemaTable( _

                        OleDb.OleDbSchemaGuid.Columns, _

                        New Object() {Nothing, Nothing, tblName})

 

        For Each dr_field As DataRow In dt_field.Rows

            Me.lstFields.Items.Add(dr_field("COLUMN_NAME").ToString)

        Next

 

    Catch ex As Exception

 

        MessageBox.Show(ex.StackTrace, "Error", MessageBoxButtons.OK,

        MessageBoxIcon.Exclamation)

 

    End Try

 

End Sub

Again, this is all pretty simple stuff, the Boolean set in the beginning of the subroutine is used to tell the application that the last item selected was a table; this is used elsewhere.  The view list handler sets this Boolean to false indicating that views are active rather than tables.

Next, the subroutine captures the selected items and sets a table name variable to contain the selected text.  A connection is created using the current database connection string.  The connection is opened, the list box is cleared of any existing content, and a data table is declared and set to hold the schema table for the current database as based on the connection.  Once that is set, the schema table's rows are looped and each column name is captured and added to the list.  At the end of it, the fields list box will contain all of the column names associated with last selected table.  The view list handler does the exact same thing.

There are a few more simple functions contained in the class but the last one that I will discuss is the handler for the context menu used to recover the field properties for a column name selected from the fields list box.  This context menu is active whenever the user selects an item from the fields list and right clicks on it.  The context menu will reveal a single option and that option will call a function used to gather a bit of information regarding the selected field such as the data type, the default value, caption, etc.  That handler looks like this:

Private Sub GetFieldInformationToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GetFieldInformationToolStripMenuItem.Click

 

        Try

 

            Dim cn As New OleDbConnection(mConnectionString)

            Dim sSql As String

 

            If mTableSelected = True Then

                sSql = "SELECT [" & Me.lstFields.SelectedItem.ToString() & "]

                FROM [" & lstTables.SelectedItem.ToString() & "]"

            Else

                sSql = "SELECT [" & Me.lstFields.SelectedItem.ToString() & "]

                FROM [" & lstViews.SelectedItem.ToString() & "]"

            End If

 

            Dim da As New OleDbDataAdapter(sSql, cn)

            cn.Open()

 

            Dim ds As New DataSet

            da.Fill(ds)

 

            Dim sb As New StringBuilder

            Dim tbl As DataTable = ds.Tables(0)

            Dim col As New DataColumn

 

            For Each col In tbl.Columns

 

                sb.Append("Field Characteristics:" & Environment.NewLine &

                Environment.NewLine)

                sb.Append("Data Type:" & Environment.NewLine)

                sb.Append(col.DataType.ToString() & Environment.NewLine &

                Environment.NewLine)

                sb.Append("Is Unique?" & Environment.NewLine)

                sb.Append(col.Unique.ToString() & Environment.NewLine &

                Environment.NewLine)

                sb.Append("Allow Nulls?" & Environment.NewLine)

                sb.Append(col.AllowDBNull.ToString() & Environment.NewLine &

                Environment.NewLine)

                sb.Append("Default:" & Environment.NewLine)

                sb.Append(col.DefaultValue.ToString() & Environment.NewLine &

                Environment.NewLine)

                sb.Append("Auto Incr:" & Environment.NewLine)

                sb.Append(col.AutoIncrement.ToString() & Environment.NewLine

                & Environment.NewLine)

                sb.Append("Caption:" & Environment.NewLine)

                sb.Append(col.Caption.ToString())

 

                MessageBox.Show(sb.ToString(), "Database Item Specification

                for Column: " & col.ColumnName.ToString(),

                MessageBoxButtons.OK, MessageBoxIcon.Information)

 

                Exit For

 

            Next

 

        Catch ex As Exception

 

            MessageBox.Show(ex.StackTrace, ex.Message, MessageBoxButtons.OK,

            MessageBoxIcon.Exclamation)

 

        End Try

 

End Sub

At the beginning of the subroutine, a select statement is formatted based upon whether or not the last item selected was a table or a view (remember the Boolean values set in the table and view list box handlers).  The  SQL statement is used along with a connection to the database to execute a query  against that database; this query pulls the field name selected from the fields list box from the last selected table or view.  A data adapter and data set are defined and populated.  Once these tasks have been accomplished, a data table is defined and set to the data set's only table, an empty data column is declared and then used to iterate through the table columns collection.  When the data column is set to the first column in the table column collection (there is only one column and one table) a string builder is configured to capture some of the column properties.  This string builder is then passed to a message box and shown to the user.  After the first pass completes, the "for" loop is manually exited.  Any errors are trapped and sent to the user in the catch block.

Figure 4:  Displaying Column Properties from the Context Menu Option

Summary.

This application is intended to demonstrate one approach to building an application capable of viewing the contents of a database dynamically and based strictly upon making a connection to either an MS Access, Oracle, or SQL Server database.  It is not the only way to accomplish this task, it is just one way to do it.  You can easily modify the approach to use other connection types, or to add new connection types, and you can modify the application to display information that I did not address in this demonstration.  I did not cover all of the code contained in the demonstration application and therefore I would recommend that you review each of the solution's classes to note the manner in which some of the unaddressed functionality was accomplished.

Login to add your contents and source code to this article
share this article :
post comment
 
Become a Sponsor
PREMIUM SPONSORS
  • Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
    Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Nevron Diagram
Become a Sponsor