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

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