Иллюстрированный самоучитель по VB.NET

       

Нетривиальный пример работы с базами данных в 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

Модуль Modulel содержит только глобальные определения различных объектов SQL, которые должны быть доступны для обеих форм. Хотя обычно подобное использование глобальных данных в окончательных версиях программ не рекомендуется, в данном случае это позволяет сосредоточить основное внимание на выполнении операций с базой данных.




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.За подробностями обращайтесь к специализированной литературе.


Содержание раздела