Почему ADO .NET — не ADO++
В каждой из
предыдущих версий VB появлялась
новая модель поддержки баз данных.
VB .NET следует этой давней традиции и
представляет новый способ работы с
данными — ADO .NET. При ближайшем
рассмотрении выясняется, что
название выбрано крайне неудачно. Почему? Потому что ADO .NET просто не является следующим поколением ADO! Это совершенно новая модель, не имеющая ничего общего с классическим вариантом ADO. В частности, для работы с результатами вам придется освоить новую объектную модель, основанную на объекте DataSet (объект ADO .NET DataSet не привязан к одной таблице и поэтому обладает значительно большими возможностями, чем, например, объект ADO RecordSet). Кроме того, модель ADO .NET:
- проектировалась
как модель с полиостью
автономной архитектурой (хотя
классы DataAdapter, Connection, Command и
DataReader остаются
ориентированными на
соединение);.
- не
поддерживает курсоры на
стороне сервера. Динамические
курсоры ADO в ней не
поддерживаются;
- базируется
на языке XML [ Во внутреннем
представлении классов ADO .NET
используется оптимизированный
формат, но весь обмен данными
происходит в формате XML. ] (что
позволяет работать через
Интернет, даже если клиент
находится за. брандмауэром (firewall));
- входит в сборку .NET System. Data. DLL, а не реализуется на уровне языка;
- вряд
ли будет поддерживать старые
клиенты Windows 95.
Еще одна
интересная особенность ADO .NET
заключается в том, что для таких
важных средств, как двухфазная
актуализация данных (commit),
потребуется использовать Enterprise
Services (то есть фактически COM+/MTS с .NET-оболочкой).
Автономные
наборы данных: новый подход к
работе с базами данных
В VB6 типичное
приложение, использовавшее базы
данных, открывало соединение с
базой и использовало его для всех
запросов на протяжении жизненного
цикла программы. В VB .NET доступ к
базам данных средствами ADO .NET
обычно основан на автономных (отсоединенных)
операциях. За этим высокопарным
выражением кроется простой смысл: в
большинстве случаев после выборки
данных из базы соединение
разрывается. В ADO .NET постоянная
связь с источником данных
встречается очень редко (при
желании вы можете использовать
постоянные соединения
классической модели ADO, прибегнув к
услугам .NET COM Interop, однако при этом
неизбежно возникают проблемы
масштабируемости, издавна присущие
ADO).
Поскольку
программа обычно работает с
автономными данными, типичному
приложению .NET для обработки
каждого запроса приходится заново
подключаться к базе данных. На
первый взгляд это кажется большим
шагом назад, но такое впечатление
обманчиво. Старый способ
поддержания соединений плохо
подходит для мира распределенных
систем: если ваше приложение
открывает соединение с базой
данных и оставляет его открытым, серверу
приходится поддерживать это
соединение до тех пор, пока клиент
его не закроет. Учитывая
интенсивную загрузку современных
серверов и пересылку огромных
объемов данных, поддержание всех
клиентских соединений
отрицательно сказывается на
пропускной способности сервера.
Кроме того, в web-комплексах
[ Web-комплексом называется группа
компьютеров, обрабатывающих трафик
одного URL. Большинство крупных
сайтов обслуживается web-комплексами,
обеспечивающими более эффективное
распределение нагрузки. ](Web farm)
запросы могут обрабатываться
разными компьютерами. Постоянные
соединения с web-комплексами
бесполезны, поскольку вы не знаете,
какой сервер будет обрабатывать
последующие запросы.
Сборка System.Data.DLL содержит большое количество классов, разделенных на пять пространств имен работы с данными с дополнительным пространством
System.Xml.
Вспомогательное пространство System.Data.SqlTypes
содержит структурные типы,
соответствующие типам данных SQL Server
(например, Sql Money и SqlDateTime).
Другое
вспомогательное пространство имен,
System.Data .Common, содержит классы, часто
используемые при обращениях к
источнику данных. В этой главе
основное внимание уделяется
пространствам имен System. Data.OleDb и System.
Data. SqlCLient, выполняющим
непосредственную работу. Классы
этих пространств имен используют
средства System. Data. Common, включая класс
DataAdapter. Класс DataAdapter представляет
соединение с базой данных,
используемое при заполнении набора
данных или обновлении источника, а
также некоторые стандартные
команды при операциях с базами
данных.
Пространство имен System.Data.OleDb
Пространство
имен System.Data.OleDb содержит классы,
используемые при взаимодействии с
OLE DB-совместимыми базами данных (такими,
как Microsoft Access или Microsoft Fox Pro). Обычно
в программах используются классы
OleDbConnectl on, OleDbCommand и OleDbDataReader этого
пространства имен. Ниже приведены
краткие описания этих важных
классов.
- Класс
OleDbConnection: можно считать, что
этот класс представляет
соединение с источником данных
OLE DC и содержит свойства,
необходимые для подключения к
базе данных (данные провайдера
OLE DB, имя пользователя и пароль).
После соединения в экземпляре
класса сохраняются
дополнительные метаданные о
базе данных.
- Класс
OleDbCommand: класс представляет
команды SQL, применяемые к базе
данных OLE DB. Вместе с командой
хранятся все параметры и
дополнительная информация,
необходимая для обработки
запроса.
- Класс
OleDbDataReader: используется после
получения данных от источника
при помощи двух классов,
описанных выше. Является
специализированной формой
класса потока ввода (см. главу 9)
и умеет только читать данные,
возвращаемые объектом OleDbCommand.
Аналогом объекта DataReader в ADO
является набор записей,
хранящийся на сервере,
доступный только для чтения и
поддерживающий только перебор
в прямом направлении.
Ниже приведен
пример использования этих трех
классов. Наше приложение
подключается к базе данных Northwind,
входящей в поставку Access и
современных версий SQL Server.
1 Imports System.Data.OleDb
2 Module Modulel
3 Sub Maint)
4 Dim myAccessConn
As OleDbConnection
5 Dim dbReader As
OleDbDataReader
6 Dim dbCmd As
OleDbCommand =New OleDbCommand(
7 "SELECT
Employees.FirstName.Employees.LastName FROM Employees")
8 Try
9 ' Открыть
соединение
10 myAccessConn =
New OleDbConnection(
11 "Provider=Microsoft.Jet.OLEDB.4.0;"
&_
12 "Data Source=C:\Program Files \Microsoft _
Office\0ffice\SamplesNNorthwind.mdb")
13 myAccessConn.Open()
14 dbCmd.Connection
= myAccessConn
15 dbReader = dbCmd.ExecuteReader(CommandBehavior.SingleResult)
16 Do While dbReader.Read()
17 Console.WriteLine(dbReader.GetString(0) & " " & _
dbReader.GetString(1))
18 Loop
19 Console.ReadLine()
20 Catch e As
Exception
21 MsgBox(e.Message)
22 End Try
23 End Sub
24 End Module
Результаты,
полученные при запуске этого
приложения, показаны на рис. 11.1.
Рис. 11.1. Результаты
выполнения простого запроса SQL
Хотя наше
приложение всего лишь выводит
список работников Northwind, его код
типичен для подключения к любой
базе данных при помощи .NET-провайдера
OLE DB, предоставленного VB .NET. В строке
1 для упрощения дальнейших ссылок
импортируется пространство имен
System. Data. 0leDb. В строках 4 и 5
объявляются две объектные
переменные. Объект 0leDbConnecti on
инкапсулирует текущее соединение к
провайдеру OLE DB и в конечном счете к
базе данных (строки 10-12). Объект 0leDbDataReader
инкапсулирует рабочие данные. В
отличие от объектов RecordSet эти
данные не обязаны относиться к
одной таблице (хотя в нашем примере
это именно так). Строка 6 определяет
запрос SQL, хранящийся в объекте
OleDbCommand. Использована версия
конструктора с параметром типа Stri
ng, в котором передается команда SQL,
— в нашем случае это простейший из
всех возможных запросов. В строке 10
создается соединение с базой
данных. При вызове конструктора
передается строка с именем
провайдера OLE DB. Значение берется из
реестра Windows и не является частью .NET
(в нашем примере используется
стандартный провайдер для Access).
Также обратите внимание на жесткую
кодировку местонахождения базы
данных Northwind; в нашем примере выбран
каталог, используемый по умолчанию
при установке Office. Если на вашем
компьютере база данных Northwind
находится в другом каталоге,
отредактируйте эту строку.
Затем созданное
соединение открывается. Поскольку
эта операция по различным причинам
может завершиться неудачей,
программный код открытия и чтения
из базы данных заключается в блок Try-Catch.
После успешного вызова Ореn() (строка
13) соединение можно использовать в
программе (выполнение этих
операций в конструкторе позволило
бы сократить программу на
несколько строк). Объект OleDbCommand
пока не знает, какое соединение он
должен использовать, поэтому
открытое соединение с базой данных
назначается свойству Connecti on
объекта OleDbCommand (строка 14). Одно из
преимуществ подобного решения
заключается в том, что оно
позволяет использовать один объект
команды с несколькими соединениями.
Команда
выполняется методом ExecuteReader()
объекта 0leDbCommand (строка 15). Мы
используем метод ExecuteReader, поскольку
остальные методы Execute возвращают
данные в формате XML и традиционные
наборы записей, обрабатываемые
менее эффективно. В строке 14
значение перечисляемого типа
CommandBehavior. SingleResul t передается методу
ExecuteReader в качестве параметра. Флаг Si
ngl eResult означает, что команда должна
выбрать из базы данных все записи
результата. Другие флаги позволяют
ограничить выборку одной или
несколькими записями. Прочитанные
записи перебираются в цикле в
строках 16-18.
Код перебора
записей эквивалентен следующему
фрагменту VB6/ADO:
Do While Not rs.EOF
Print rs(0)
rs.MoveNext() Loop
Использование
метода Read предотвращает одну
распространенную ошибку, часто
допускаемую программистами VB6 ADO,
которые забывают перейти к
следующей записи методом MoveNext. Все
операции с одной записью
выполняются между вызовами Read,
поскольку после вызова Read
вернуться к содержимому предыдущей
записи уже не удастся.
В цикле
вызываются различные методы GetXXX
объекта 01 eDbReader, возвращающие
значение поля с заданным индексом (нумерация
полей записи начинается с 0). Таким
образом, вызов
dbReader.GetString()
возвращает
значение второго столбца в
формате String. Вместо индекса при
вызове GetStrlng можно указать имя
столбца, но этот вариант менее
эффективен.
Чтение данных
из базы SQL Server происходит
аналогичным образом —
пространства имен OleDb и SqlClient имеют
практически одинаковый синтаксис.
Ниже приведена версия предыдущей
программы для SQL Server:
Imports System.Data.SqlClient
Module Modulel
Sub Main()
Dim mySQLConnString
As String
Dim mySQLConn As
SqlConnection
Dim dbReader As
SqlDataReader
Dim dbCmd As
SqlCommand = New SqlCommand(
"SELECT
Employees.FirstName.Employees.LastName FROM
Employees") Try
mySQLConnString = _
"uid-test:password=apress;
database=northwind:server=Apress"
mySQLConn = New
SqlConnection(mySQLConnString)
mySQLConn.Open()
dbCmd.Connection =
mySQLConn
dbReader = dbCmd.ExecuteReader(CommandBehavior.SingleResult)
Do While dbReader.Read()
' Вывести данные в консольном окне
Console.WriteLinetdbReader.GetString(0) & "." &_
dbReader.GetString(1))
Loop
Catch e As Exception MsgBox(e.Message)
End Try
Console. ReadLine()
End Sub
End Module
Основное
различие (помимо имен классов)
наблюдается в формате строки
соединения. Предполагается, что на
сервере Apress имеется учетная запись
с паролем apress. При подключении к SQL
Server в строке соединения
указывается идентификатор
пользователя, пароль, сервер и имя
базы данных. Передавая эту
информацию, мы получаем объект
соединения. Конечно, лишь
простейшие запросы формулируются в
виде простой строки; в любом
сколько-нибудь нетривиальном
случае строку запроса приходится
строить из отдельных фрагментов.
Вызов хранимой процедуры
В следующем примере используется
хранимая процедура с именем getalbumname.
Процедура вызывается с одним
параметром и выбирает из базы
данных albums запись альбома с
заданным именем:
create
procedure getalbumbyname
@albumname varchar(255) As
select *from albums where albumname = @albumname
Выборка данных с использованием
хранимой процедуры организована
аналогично простому запросу к базе
данных Northwind:
Dim dbCmd
As SqlCommand = New SqlCommand(
"execute getalbumbyname 'Operation Mindcrime'")
Try
mySQLConn =New SqlConnection(
"user id=sa:password=password;" & _
"database=albums;server=i-ri3")
mySQLConn.Open()
dbCmd.Connection = mySQLConn
dbReader = dbCmd.ExecuteReader(CommandBehavior.SingleResult)
' И т.д.
End
Try
Как видите, программа почти не
изменилась, разве что команда SQL,
использовавшаяся для создания
объекта Sql Command, превратилась в
команду вызова хранимой процедуры
getalbumbyname, которой в качестве
параметра передается имя
интересующего нас альбома. Конечно,
после вызова ExecuteReader цикл перебора
записей не нужен, поскольку мы
точно знаем, что хранимая процедура
возвращает всего одну запись.
Нетривиальный
пример работы с базами данных в 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
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
Нетривиальный пример работы с базами данных в 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. За
подробностями обращайтесь к
специализированной литературе.