65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 VB 2005 查看数据库结构的途径

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.55/5 (15投票s)

2006年9月14日

12分钟阅读

viewsIcon

56476

downloadIcon

333

本文介绍了一种方便地检查数据库中所有表、视图和列的简单方法。

Screenshot

引言

本文介绍了一种方便地检查数据库中所有表、视图和列的简单方法。本文旨在描述一个可用于连接数据库(Oracle、SQL Server 或 MS Access)、查看该数据库的表和视图,并深入到每个表或视图以生成其包含的所有列列表的示例应用程序。此外,该应用程序还将允许用户检查与每个列关联的定义(例如,其数据类型、标题、默认值等)。

该应用程序没有特定目的,并且只有几个关键方法与之关联。尽管该应用程序不执行任何有用的任务,但它可以作为某些有用工具的基础,例如一个可以映射一个数据库表中的字段到另一个数据库表中的字段的工具,或者它可以作为允许用户创建临时查询的工具的基础。

图 1:演示应用程序正在运行

入门

要开始,请解压缩包含的项目,然后在 Visual Studio 2005 环境中打开解决方案。在解决方案资源管理器中,您应该会注意到三个重要文件:

  • AppVars.vb:包含模块中的一些应用程序范围变量。
  • frmMain.vb:包含主应用程序和大部分代码。
  • frmConnect.vb:包含用于连接到数据库的对话框。

应用程序变量

让我们先从提到的第一个项目文件 AppVars.vb 开始。在 IDE 中打开该文件进行查看。这只是一个代码模块,它不包含任何方法,只有一组在 frmConnect.vbfrmMain.vb 之间共享的变量。整个代码体如下:

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

定义的第一个变量用于存储数据模型名称。这仅是任意的,在此应用程序中没有实际用途;它仅用作标识连接类型的标签。

下一个变量用于存储数据库类型,例如 MS Access 或 SQL Server。

连接字符串变量,正如您可能猜到的,存储连接字符串。

初始目录变量仅用于 SQL Server 连接,它包含,是的,初始目录名称。

提供程序字符串包含数据提供程序的名称。

gUserIDgPassword 非常容易理解;在实际应用中,您可能希望加密密码值,事实上,您可能希望将所有这些项存储在应用程序中作为资源或其他格式,而不是将其放入模块文件中;但为了本示例的方便,模块就足够了。

gCurrentTablesgCurrentViews 是字符串数组,用于捕获与连接关联的所有表和视图。添加这些是为了将来可能需要持久化这些项,并在某些更有用的应用程序中更快地将其提供给控件。我确实填充了这些字符串数组,但在此演示应用程序中我并没有真正使用它们。

虽然此应用程序确实使用了带有这些变量的模块,但更好的方法是创建一组可序列化的类,其中包含每种数据库类型的属性,并在给定当前连接的情况下,实例化、填充和序列化正确的类类型。

连接对话框

连接对话框包含在 frmConnect.vb 中;此对话框用于捕获创建与 Oracle、SQL Server 或 MS Access 数据库的有效连接所需的变量。该对话框包含一个带有三个面板的选项卡式窗格,每个面板对应一种连接类型。每个面板都包含生成连接所需的所有控件。用户可以从此对话框测试连接,一旦用户接受对话框,连接信息将被持久化并提供给应用程序。

图 2:显示 SQL Server 选项的连接对话框

代码非常简单。如果您愿意在 IDE 中打开代码视图,您将看到代码文件如下开始:

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

请注意,导入包括 SerializationIO;这些用于持久化连接信息。在声明区域,请注意声明了 AppVars.vb 文件中使用的变量的本地副本(不包括视图和表数组)。这些是将在该类中使用的成员变量。

对于每个“确定”按钮(支持的每种数据库类型都有一个),都将编写一个处理程序,该处理程序基本上遵循以下代码块:

    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

此处理程序首先捕获对话框中的用户输入,并将本地变量设置为表单值;我没有在示例中这样做,但您应该验证所有这些输入。接下来,处理程序将尝试使用这些值创建连接对象,并尝试打开此连接以确认其有效。如果尝试失败,将通知用户;如果成功,值将添加到哈希表中,然后将哈希表序列化并持久化到应用程序路径中的一个文件中;这发生在 SerializeAppVars() 调用中。序列化发生后,视图和表名称将通过 StoreViewNames() 调用存储,最后,表单将被 Me.Dispose 调用处理。

对话框的三个选项卡都有一个“确定”按钮,每个按钮的处理方式都与此方法一致。检查每个特定的处理程序以观察与该特定选项卡的变量相关的细微差别。

StoreViewNames() 子例程用于持久化捕获的值,以便在下次应用程序启动时可以恢复它们。这段代码更有趣:

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

在这段代码中,请注意声明了一个名为“SchemaTable”的 DataTable;在建立 OleDbConnection 后,请注意将该表赋值给保存连接的架构表的代码;正是这个表使得检查数据库结构及其内容变得容易。在设置好架构表后,其余代码会遍历表的行,首先查找表,然后查找视图。找到的表被添加到包含表名称的字符串数组中,找到的视图被添加到包含视图名称的字符串数组中。

一旦数组设置好,子例程就会将这两个数组传递给 AppVars 的当前视图和当前表变量,然后结束。

SerializeAppVars() 子例程用于将变量持久化到应用程序路径中存储的文件中;此文件用于允许应用程序在最后一个已建立的连接已到位的情况下打开。用于序列化值的代码如下:

    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

如您所见,这是一个非常简单的子例程。它接受在“确定”按钮事件处理程序中生成的哈希表,打开到名为“appset.con”的文件的文件流,该文件位于应用程序启动路径中,并使用二进制格式化器序列化哈希表并将其存储在文件中。

对话框的三个选项卡也都包含一个测试按钮。与“确定”按钮一样,所有三个测试按钮的功能几乎相同,因此我只在此文档中展示其中一个。请查看示例项目以了解三个处理程序之间的差异,但请注意,基本上只有连接字符串的变量和格式会因按钮而异。Oracle 测试按钮的处理程序如下所示:

    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

奇怪的是,我使用了 ADO 连接来执行测试,您可以将其替换为 OleDbConnection 对象并执行相同的测试。代码很简单;您会注意到表单变量被收集并传递给本地变量。这些变量用于格式化连接字符串。连接字符串被打开作为测试;如果成功打开,测试通过;如果不成功,测试失败。用户将被告知任一结果。

连接对话框中的其余代码相当标准,用于执行诸如关闭表单或浏览文件等操作。请查看 IDE 中类的内容以了解其余内容;有足够的注释使其易于理解。

主窗体

主应用程序包含在 frmMain 类中。此表单用于访问连接对话框,并将数据库信息加载到表单控件中。表单结构顶部有一个菜单;此菜单包含退出应用程序、创建新连接、查看当前连接以及加载当前数据库关联的架构信息的选项。在表单的主区域,有三个分组框,一个包含一个列表框控件,用于显示当前数据库中的表,一个包含一个列表框控件,用于显示当前数据库中的视图,还有一个包含一个列表框控件,用于显示从表或视图列表框控件中选择的任何视图或表中的列。

图 3:主表单激活 SQL Server 连接

该类从以下代码开始:

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

在导入部分,请注意数据和相关类库的添加;您可能需要打开项目引用并为项目添加任何缺失的引用,如果加载应用程序到 IDE 时不支持任何这些导入。导入后,添加了一个声明区域并声明了三个变量。这些变量随后在此类中使用。

图 4:演示项目引用

下一个值得关注的代码部分是表单加载事件处理程序,它看起来像这样:

    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

此事件处理程序很简单;如果存在现有连接文件,则将其打开并反序列化。表单标签会更新以显示当前数据库类型,如果连接字符串存在,则将其传递给一个名为 OpenTablesOnStart 的子例程,该子例程随后会将当前连接关联的表和视图加载到表和视图列表框控件中。

由表单加载事件处理程序调用的 OpenTablesOnStart 子例程用于填充表和视图列表框,其内容如下:

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

快速扫描子例程将发现它以与 frmConnect 类中用于填充视图和表数组的子例程一致的方式运行。同样,声明了一个 DataTable 变量,并将其设置为包含目标数据库的架构。然后该表会循环两次,一次用于捕获表,一次用于捕获视图。收集到的视图和表会放入主表单上的列表框中。

包含视图和表的每个列表框都有一个处理程序,用于从该视图或表中恢复列并将列集合显示在字段列表框中。两个处理程序的操作方式基本相同;以下是一个示例,显示了表列表的处理程序:

    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

同样,这一切都非常简单。子例程开头设置的 Boolean 值用于告知应用程序最后选择的项目是表;这将在其他地方使用。视图列表处理程序将此 Boolean 设置为 False,表示视图处于活动状态而不是表。

接下来,子例程捕获选定的项并将表名变量设置为包含选定的文本。使用当前数据库连接字符串创建连接。打开连接,清除列表框中的任何现有内容,然后声明一个 DataTable 并将其设置为基于连接的当前数据库的架构表。设置好后,循环架构表的行,捕获每个列名并将其添加到列表中。最后,字段列表框将包含与最后选择的表相关的所有列名。视图列表处理程序执行完全相同的操作。

类中还有一些更简单的函数,但我将讨论的最后一个是用于恢复选定字段名称的属性的上下文菜单的处理程序。当用户从字段列表中选择一个项并右键单击它时,此上下文菜单将变为活动状态。上下文菜单将显示一个选项,该选项将调用一个函数,该函数用于收集有关选定字段的一些信息,例如数据类型、默认值、标题等。该处理程序如下所示:

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

在子例程开始时,会根据最后选择的项目是表还是视图(还记得表和视图列表框处理程序中设置的 Boolean 值吗?),格式化一个 SELECT 语句。SQL 语句与数据库连接一起用于对该数据库执行查询;此查询从最后一个选定的表或视图中提取字段列表中选定的字段名。定义并填充数据适配器和数据集。完成这些任务后,定义一个数据表并将其设置为数据集的唯一表,声明一个空的 DataColumn,然后使用它来迭代表列集合。当 DataColumn 设置为表列集合中的第一个(有一个列和一个表)时,会配置一个 StringBuilder 来捕获一些列属性。然后将此 StringBuilder 传递给消息框并显示给用户。第一次遍历完成后,For 循环会手动退出。在 Catch 块中捕获并发送任何错误给用户。

图 4:从上下文菜单选项显示列属性

摘要

该应用程序旨在演示一种构建能够动态查看数据库内容的应用程序的方法,并且严格基于连接到 MS Access、Oracle 或 SQL Server 数据库。这不是完成此任务的唯一方法,它只是一种方法。您可以轻松地修改该方法以使用其他连接类型,或添加新的连接类型,并且您可以修改应用程序以显示我在本演示中未涵盖的信息。我没有涵盖演示应用程序中包含的所有代码,因此我建议您查看解决方案中的每个类,以了解某些未涵盖的功能是如何实现的。

© . All rights reserved.