Нетривиальный пример работы с базами данных в VB .NET (часть 1)
В этом разделе представлено графическое приложение, при помощи которого пользователь может подключиться к выбранной базе данных SQL, выполнить запрос и получить его результаты в виде списка. Простоты ради мы отказались от проверки пользовательского ввода. Программа состоит из трех файлов: двух форм (frmMain и frmResults, см. рис. 11.2 и 11.3 соответственно) и стандартного модуля Modulel.
Несмотря на свою длину, программа не содержит ничего принципиально нового. На главной форме размещены четыре текстовых гюля для ввода имени сервера, имени базы данных, идентификатора пользователя и пароля. При нажатии кнопки Connect программа динамически выполняет введенную команду во фрагменте, выделенном жирным шрифтом.
Рис. 11.2. Главная форма приложения
Рис. 11.3. Форма результатов приложения
'frmMain.vb
Imports System.Data.SqlClient
Public Class frmMain
Inherits System.Windows.Forms.Form #Region "Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'Вызов необходим для работы дизайнера форм Windows
InitializeComponent()
' Дальнейшая инициализация выполняется
' после вызова InitializeComponent()
End Sub
' Форма переопределяет Dispose для очистки списка компонентов.
Protected Overloads Overrides
Sub Dispose(ByVal disposing As Boolean)
If Disposing Then
If Not (components Is Nothing) Then
components. Dispose()
End If
End If
MyBase.Dispose(Disposing) End Sub
Private WithEvents Label1 As System.Windows.Forms.Label
Private WithEvents Label2 As System.Windows.Forms.Label
Private WithEvents Label3 As System.Windows.Forms.Label
Private WithEvents Label4 As System.Windows.Forms.Label
Private WithEvents btnConnect As System.Windows.Forms.Button
Private WithEvents txtUID As System.Windows.Forms.TextBox
Private WithEvents txtPassword As System.Windows.Forms.TextBox
Private WithEvents txtDatabase As System.Windows.Forms.TextBox
Private WithEvents txtServer As System.Windows.Forms.TextBox
' Необходимо для работы дизайнера форм Windows
Нетривиальный пример работы с базами данных в VB .NET (часть 2)
Вероятно, наибольший интерес представляет форма frmResults (комментарии следуют после листинга). Ключевое место в этой форме занимает метод btnQuery_Click, выделенный жирным шрифтом:
' frmResults.vb
Imports System.Data.SqlClient
Public Class frmResults
Inherits System.Windows.Forms.Form fRegion "Windows Form Designer generated code "
Public Sub New() MyBase.New()
'Вызов необходим для работы дизайнера форм Windows
InitializeComponent()
' Дальнейшая инициализация выполняется
' после вызова InitializeComponent()
End Sub
' Форма переопределяет Dispose для очистки списка компонентов.
Public Overrides Sub Dispose()
MyBase.Dispose()
If Not (components Is Nothing) Then components.
Dispose()
End If
End Sub
Private WithEvents txtQuery As System.Windows.Forms.TextBox
Private WithEvents btnQuery As System.Windows.Forms.Button
Private WithEvents IstData As System.Windows.Forms.ListBox
' Необходимо для работы дизайнера форм Windows
Private components As System.ComponentModel.Container
' ВНИМАНИЕ: следующий фрагмент необходим для дизайнера форм Windows
' Для его модификации следует использовать дизайнер форм.
' Не изменяйте его в редакторе!
<System.Diagnostics.DebuggerStepThrough()>
Private Sub _
Initial izeComponent()
Me.btnQuery = New System.Windows.Forms.Button()
Me.txtQuery = New System.Windows.Forms.TextBox()
Me.IstData = New System.Windows.Forms.ListBox()
Me.SuspendLayout()
'btnQuery
Me. btnQuery. Font = NewSystem. Orawing. Font ("Microsoft Sans Serif"._
8.5!.System.Drawing.FontStyle.Regular,
System.Drawing.GraphicsUnit.Point,CType(0. Byte))
Me.btnQuery.Location = New System.Drawing.Point(440. 0)
Me.btnQuery.Name = "btnQuery"
Me.btnQuery.Size = New System.Drawing.Size(56. 24)
Me.btnQuery.Tablndex = 2
Me.btnQuery.Text = "&Execute"
'txtQuery
Me. txtQuery. Font=New System. Drawing. Font ("Microsoft Sans Serif", _
Private components As System.ComponentModel.Container
' ВНИМАНИЕ: следующий фрагмент необходим для дизайнера форм Windows
' Для его модификации следует использовать дизайнер форм.
' Не изменяйте его в редакторе!
<System.Diagnostics.DebuggerStepThrough()>
Private Sub _ Initial izeComponent()
Me.Label4 = New System.Windows.Forms.Label ()
Me.txtPassword = New System.Windows.Forms.TextBox()
Me.Label 1 = New System.Windows.Forms.Label ()
Me.txtServer = New System.Windows.Forms.TextBox()
Me.Label2 = New System.Windows.Forms.Label ()
Me.Labels = New System.Windows.Forms.Label ()
Me.txtUID - New System.Windows.Forms.TextBox()
Me.txtDatabase = New System.Windows.Forms.TextBox()
Me.btnConnect = New System.Windows.forms.Button()
Me.SuspendLayout()
'Label4
Me.Label4.Location = New System.Drawing.Point(24.176)
Me.Label 4.Name = "Label4"
Me.Label4.Size = New System.Drawing.Size(82.19)
Me.Label4.TabIndex = 0
Me.Label4.Text = "Password:"
Me.Label4.TextAlign = System.Drawi ng.ContentAlignment.MiddleRight
'txtPassword
Me.txtPassword.Location = New System.Drawing.Point(168.168)
Me ..txtPassword. Name = "txtPassword"
Me.txtPassword.PasswordChar = ChrW(42)
Me.txtPassword.Size = New System.Drawing.Size(205.22)
Me.txtPassword.Tablndex = 3
Me.txtPassword.Text = ""
'Label 1
Me.Label 1.Location = New System.Drawing.Point(24. 32)
Me.Label 1.Name = "Label1"
Me.Label 1.Size = New System.Drawing.SizeC82. 20)
Me.Label 1.Tablndex =0
Me.Label 1.Text = "Server:"
Me.Label 1.TextAli gn = System.Drawi ng.ContentAlignment.Mi ddleRight
'txtServer
Me.txtServer.Location - New System.Drawing.Point(168, 24}
Me.txtServer.Name = "txtServer"
Me.txtServer.Size = New System.Drawing.Size(205. 22)
Me.txtServer.Tablndex = 0
Me.txtServer.Text = ""
'Label 2
Me.Label2.Location = New System.Drawing.Point(24. 80)
Me.Label 2.Name = "Label 2"
Me.Label2.Size = New System.Drawing.Size(82, 20)
Me.Label2.Tablndex = 0
Me.Label 2.Text = "Database:"
Me.Label 2.TextAlign = System.Drawi ng.ContentAlignment.Mi ddleRight
'Label3
Me. Labels.Anchor = System.Windows.Forms.AnchorStyles.None
Me.Label3.Location = New System.Drawing.Point(24. 128)
Me.Labels.Name = "Label 3"
Me.Labels.Size = New System.Drawing.Size(82. 20)
Me.Labels.Tablndex = 0
Me.Labels.Text = "User ID:"
Me.Label 3.TextAli gn = System.Drawi ng.ContentAlignment.Mi ddleRi ght
'txtUID
Me.txtUID.Location = New System.Drawing.Point(168, 120)
Me.txtUID.Name = "txtUID"
Me.txtUID.Size - New System.Drawing.Size(205, 22)
Me.txtUID.Tablndex = 2
Me.txtUID.Text = ""
'txtDatabase
Me.txtDatabase.Location = New System.Drawing.Point(168. 72)
Me.txtDatabase.Name = "txtDatabase"
Me.txtDatabase.Size = New System.Drawing.Size(205. 22)
Me.txtDatabase.Tablndex = 1
Me.txtDatabase.Text = ""
'btnConnect
Me.btnConnect.Location = New System.Drawing.Point(160. 232)
Me.btnConnect.Name = "btnConnect"
Me.btnConnect.Size = New System.Drawing.Size(92, 30)
Me.btnConnect.Tablndex = 4
Me. btnConnect.Text = "SConnect"
'frmMain
Me.AutoScaleBaseSize = New System.Drawing.Size(6. 15)
Me.ClientSize - New System.Drawing.Size(408, 280)
Me.Controls.AddRange(New _
System.Wi ndows.Forms.Control(){Me.btnConnect,_
Me.txtPassword. Me.txtUID. Me.txtDatabase.
Me.txtServer.Me
.Label 4.
Me.Label3.Me
.Label 2.
Me.Label 1})
Me.Name - "frmMain" Me.Text = "DB Connector"
Me.ResumeLayout(False) End Sub
#End Region
Private Sub btnConnect_C1ick( ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles btnConnect.Click
Try
mySQLConn = New SqlConnectionC'user id=" & txtUID.Text &
";password="&txtPassword.Text & _ ";database="&txtDatabase.Text & _
";server="&txtServer.Text)
mySQLConn.Open() dbCmd.Connect!on = mySQLConn
Dim frmChild As New frmResults() frmChild.Show()
Catch except As Exception MsgBox(_
" Failed to connect for the following reason:<" & _ except.Message & ">")
End Try
End Sub
End Class
Модуль содержит следующий код:
Imports System.Data.SqlClient Module main
' Глобальные определения
Public mySQLConn As SqlConnection
Public dbReader As SqlDataReader
Public dbCmd As SqlCommand = New SqlCommand()
End Module
8.5!. System.Drawing.FontStyle.Regular.
System.Drawi ng.Graphi csUnit.Point.CTypet 0. Byte))
Me.txtQuery.Location = New System.Drawing.Point(8. 0)
Me.txtQuery.Name = "txtQuery"
Me.txtQuery.Size = New System.Drawing.Size(432, 20)
Me.txtQuery.Tablndex = 1
Me.txtQuery.Text = "TextBox1"
'IstData
Me.lstData.ColumnWidth = 120
Me.IstData.Location = New System.Drawing.Point(8. 32)
Me.lstData.MultiColumn = True
Me.lstData.Name = "IstData"
Me.lstData.Size = New System.Drawing.Size(488. 355)
Me.lstData.Tablndex = 3
'frmResults
Me.AutoScaleBaseSize = New System.Drawing.Size(5. 13)
Me.ClientSize = New System.Drawing.Size(504. 397)
Me.Controls.AddRange(New System.Windows.Forms.Control()
{Me.lstOata. Me.btnQuery, Me.txtQuery})
Me.Name = "frmResults"
Me.Text = "Query Window"
Me.ResumeLayout(False)
End Sub
#End Region
Private Sub btnQuery_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles btnQuery.Click
Try
dbCmd.CommandText = txtQuery.Text
dbReader=dbCmd. ExecuteReader (CoimandBehavior. Singl eResult)
' Получить схему таблицы
Dim dtbllnfo As DataTable = dbReader.GetSchemaTable()
' Служебная переменная для перебора записей
Dim rwRow As DataRow
Dim strHeaders As System.Text.StringBuilder - _
New System.Text.StringBuilder()
Dim strData As System.Text.StringBuilder = New _
System.Text.StringBuilder()
Dim typTypesCdtbllnfo.Columns.Count) As Type
Dim intCounter As Integer = 0
' Перебрать все записи метаданных
For Each rwRow In dtblInfo.Rows
' Определить тип
typTypes(intCounter)= rwRow("DataType") intCounter +=1
' Включить в строку имя поля
strHeaders.Append("<" & rwRow(0) & ">" & vbTab) Next
' Занести в список заголовочную строку
1stData.Items.Add(strHeaders.ToString())
' Перебор записей данных
Do While dbReader.Read()
' Перебор полей записи
For intCounter = 0 To (dbReader.FieldCount - 1)
' Включить содержимое поле в выходную строку
strData.Append(GetProperType(dbReader,intCounter,_
typTypes(intCounter)) & vbTab) Next
' Включить строку в список
1stData.Items.Add(strData.ToString())
' Очистить объект StringBuilder strData = New System.Text.StringBuilder()
Loop Catch except As Exception
MsgBoxt"Error:" & except.Message)
End Try
End Sub
' Функция получает данные конкретного столбца.
Private Function GetProperType(ByVal dr As SqlDataReader.
ByVal intPos As Integer, ByVal typType As Type) As Object
' Проверить тип поля, затем получить значение Select
Case typType.Name Case "String"
' Преобразовать и вернуть
Return CType(dr.GetString(intPos).String)
Case "Int32"
' Преобразовать и вернуть
Return
CType(dr.Get!nt32(intPos). Int32)
' Здесь следовало бы организовать проверку всех
' остальных типов и возврат соответствующих значений.
' Мы выбрали простой путь и ограничились проверкой
' двух самых распространенных типов
Case Else
Return "<Unsupported Type>"
End Select
End Function
End Class
'При нажатии кнопки в объект команды SQL
'заносится текст, введенный пользователем в текстовом поле:
dbCmd.CommandText = txtQuery.Text
(в настоящем примере пропущена проверка данных, необходимая в любой реальной программе).
Далее объявляются объекты, используемые при чтении и выводе имен полей и их значений:
Dim dtbllnfo As DataTable = dbReader.GetSchemaTable()
Dim rwRow As DataRow
Dim strHeaders As System.Text.StringBuilder = New _
System.Text.StringBuilder()
Dim strData As System.Text.StringBuilder = New _
System.Text.Stri ngBui1der()
Dim typTypes(dtblInfo.Columns.Count) As Type
Поскольку в этом приложении структура базы данных не известна заранее, мы получаем ее описание при помощи метода GetSchemaTable(). Этот метод возвращает объект DataTable с метаданными (описаниями полей записей полученного набора). Метаданные содержат информацию о количестве полей в записи, их именах и типах. На основании этой информации можно запросить и вывести данные из любой доступной базы данных.
Помните, что в режиме Option Strict On (который всегда должен быть активным) для вызова правильной функции GetXXX() объекта DataReader необходимо знать тип поля. По соображениям эффективности в приведенном примере использованы две переменные типа Stri ngBui I der (см. ниже). Информация, необходимая для вывода данных в списке, извлекается в цикле:
Dim intCounter As Integer =0 For Each rwRow In dtbllnfo.Rows
typTypes(intCounter) = rwRow("'DataType")
intCounter += 1
strHeaders.Append("<" & rwRow(0) & ">" & vbTab) Next
Записи DataTable перебираются в цикле For Each. Типы полей сохраняются в массиве typTypes и затем присоединяются к объекту StringBuilder для последующего вывода всех имен столбцов за одну операцию (однократное обновление свойства выполняется быстрее многократных). Также обратите внимание на использование имени поля в вызове rwRow( "DataType") — структура таблицы может измениться, что приведет к изменению номера поля DataType. После завершения цикла у нас появится вся необходимая информация об именах и типах всех полей, и мы сможем перейти к ее выводу в конструкции с вложенным циклом:
Do While dbReader.Read()
For intCounter = 0 To (dbReader.FieldCount = 1)
strData.Append(GetProperType(dbReader.intCounter,
typTypes(intCounter)) & vbTab) Next
1stData.Items.Add(strData.ToString()) strData = New
System.Text.StrlngBuilder() Loop
Первая часть цикла напоминает аналогичные конструкции из предыдущих примеров — мы перебираем все поля записи, определяем тип каждого поля и выводим данные в списке перед следующим вызовом Read. Для упрощения этой задачи была написана вспомогательная функция GetProperType().
На рис. 11.4 показан результат выборки данных из базы Northwind.
Рис. 11.4. Результат обработки запроса к базе данных Northwind
В этой главе мы постарались дать представление о работе с ADO .NET, однако читатель должен помнить, что перед ним лишь предельно краткий обзор. В частности, мы совершенно не коснулись таких тем, как обновление данных в хранимых процедурах, элементы, связанные с данными, или объекты DataAdapter/DataSet.За подробностями обращайтесь к специализированной литературе.