Продолжается подписка на наши издания! Вы не забыли подписаться?

Создание middleware-компонентов доступа к данным

Введение

Система разработки Visual Basic 6.0 и ADO 2.0 предоставляют разработчикам серверных компонентов множество интересных возможностей . Цель этой статьи — познакомить читателя со средствами и методикой, доступными при разработке middle-tier компонентов доступа к данным с помощью Visual Basic и ADO. Компоненты доступа к данным позволяют бизнес-компонентам связываться с базовыми сервисами данных. В этой статье рассказывается о предназначении компонентов доступа к данным, как они встраиваются в архитектуру Windows DNA (Distributed interNet Applications) и о том, какие особенности Visual Basic 6.0 и ADO 2.0 могут помочь при разработке таких компонентов. После рассмотрения доступных средств мы обратимся к двум способам разработки компонентов доступа к данным с использованием этих средств.

Единого верного пути при разработке компонентов доступа к данным, или какого-то конкретного рецепта нет. Но в статье можно почерпнуть сведения о способах и средствах создания компонентов для отдельно взятой реальной среды разработки.

В какое место Windows DNA-приложения встраиваются компоненты доступа к данным?

Компонент доступа к данным — это не то же самое, что механизм доступа к данным. Механизм доступа — это интерфейс прикладного программирования (API), используемый для связи с сервисами данных (например, в этой статье мы рассматриваем — Microsoft SQL Server™). Компоненты доступа к данным предоставляют надежный интерфейс для взаимодействия компонентов приложений с данными. Они изолируют разработчика компонента приложения от основного механизма доступа к данным.

Windows DNA распределяет сервисы приложений между тремя логическими уровнями сервисов:

  1. Пользовательские сервисы (User services) — отвечают за пользовательский интерфейс (презентация)
  2. Бизнес-сервисы (Business services) — отвечают за реализацию бизнес-правил
  3. Сервисы данных (Data services) — отвечают за доступ к постоянным (persistent) данным системы и их обслуживание

В Windows DNA-приложении компоненты связываются, используя многокомпонентную модель (Component Object Model). После этого компонент доступа к данным предоставляет базовому хранилищу данных интерфейс COM-объекта. Обычно бизнес-компоненты представляют собой потребителя компонента доступа к данным. В качестве хранилища данных выступает система управления реляционными базами данных (RDBMS) — например, SQL Server или Oracle, но это может быть и какое-то нетрадиционное хранилище, например, Microsoft Exchange или Microsoft Active Directory.

Для чего же нужно отделять сервисы данных от бизнес-сервисов? Почему бы не обеспечить бизнес-компоненту прямой доступ к базовому хранилищу данных, например, с помощью команд ADO SQL? Есть множество причин, чтобы инкапсулировать хранилища данных отдельно от базового уровня COM. Такая инкапсуляция:

Требования MTS к доступу к данным

Обычно разработчики проектируют и разрабатывают компоненты доступа к данным так же, как и бизнес-компоненты. Поэтому компоненты доступа к данным в обычно помещаются в ActiveX DLL-и, и запускаются под управлением Microsoft Transaction Server (MTS), являющегося частью сервера Windows NT.

MTS обеспечивает поддержку транзакций для ActiveX-компонентов, а также среду для запуска удаленных компонентов. Чтобы полностью использовать преимущества MTS-среды, компонент должен придерживаться правил, определяемых MTS.

В общем, существует несколько путей получения компонентом доступа к данным. Некоторые из них, такие как ADO, Remote Data Objects (RDO), Data Access Objects (DAO), и Open Database Connectivity (ODBC) API, позволяют разработчикам на Visual Basic осуществлять доступ к реляционным базам данных.

При выборе механизма доступа к данным следует применять следующие критерии:

  1. Объектная ориентация. Разработчик приложения должен использовать объектную модель.
  2. Высокое быстродействие. Механизм доступа к данным должен обладать высоким быстродействием.
  3. Возможности. Механизм доступа к данным должен поддерживать расширенную функциональность предлагаемую хранилищами данных.

OLE DB и ADO — это механизмы доступа к данным, которые Микрософт рекомендует для разработки приложений. OLE DB — это стратегическая парадигма доступа к данным Microsoft, обеспечивающая быстрый доступ к любым источникам данных и позволяющая унифицировать доступ к любым данным. ADO — это интерфейс прикладного программирования, основанный на OLE DB и предоставляющий легкий в использовании совместимый с OLE Automation интерфейс к инфраструктуре, обеспечиваемой OLE DB-провайдерами.

ADO предоставляет много преимуществ, большинство которых реализуются recordset-объектами, предоставляющими как клиентским, так и серверным компонентам возможность взаимодействия с данными.

Объект ADO Recordset — это аналог resultset-объектов в RDO/DAO. Recordset представляет данные в форме записей и полей. При работе с реляционными источниками данных, записи и поля транслируются в столбцы и строки SQL-операторов. Кроме того, recordset обеспечивают поддержку исходных (before) и измененных (after) значений полей, не нуждаясь в дополнительном вмешательстве со стороны разработчика.

Возможности, касающиеся разработки компонентов доступа к данным

Visual Basic 6.0 и ADO 2.0 имеют множество возможностей и средств, предназначенных для разработчика Windows DNA-приложений. В этом разделе мы остановимся на тех возможностях, которые относятся к разработке компонентов доступа к данным.

Disconnected Recordsets

Перемещение информации между компонентами клиента и сервера — это важная составляющая проектирования распределенных приложений. При этом могут возникнуть две проблемы:

Ресурсы сервера (например, подключение к базе данных) используются неэффективно, ограничивая масштабируемость.
Получение данных приводит к большому числу сетевых обменов между клиентом и сервером, что ограничивает быстродействие.

ADO предлагает возможность обмена данными между машинами клиента и сервера в виде наборов записей (recordset). Приложение сервера посылает данные клиентскому компоненту и отключает соединение с базой данных. После этого клиентский компонент, совершив манипуляции с данными, возвращает приложению сервера обновленную информацию, причем все это делается одним сетевым обменом.

Для отключения recordset должны быть заданы значения двух параметров. Во-первых, значением CursorLocation должно быть adUseClient. Это говорит ADO, что вместо курсоров со стороны сервера надо использовать библиотеку курсоров со стороны клиента.

Во-вторых, после считывания recordset-а свойству ActiveConnection необходимо присвоить значение Nothing, это заставит ADO игнорировать информацию о первоначальном соединении recordset-а. Если свойству ActiveConnection это значение не присвоено, клиентское приложение может, минуя бизнес-компоненты, напрямую обновить сервисы данных с помощью методов Update и UpdateBatch.

Если клиентское приложение должно иметь возможность вносить изменения в набор записей, получаемый из бизнес-компонента, должно быть задано дополнительное свойство recordset. Для того, чтобы recordset не был предназначен только для чтения, или чтобы предупредить попытку подключения к БД при каждом изменении значения, свойству LockType должно быть присвоено значение adBatchOptimistic. Это позволяет клиентскому приложению производить множественные изменения перед возвратом recordset-а бизнес-компоненту .

Иерархические recordset

При разработке Windows DNA-приложения следует принять общее решение о том, как обрабатывать связи между сущностями типа главный/подчиненный. Примером такой связи может быть связь между заказом и его позициями. Часто эти связи реализуются как зависимые объекты или зависимые наборы (collections) объектов. До появления ADO 2.0 для реализации связей главный/подчиненный требовалось много работы.

“Иерархический Курсор” (Hierarchical Cursor) ADO 2.0 позволяет создавать связи типа главный/подчиненный. Иерархический recordset может использоваться для предоставления зависимых объектов и списков объектов без создания бизнес-компонентов.

Для предоставления возможности связи иерархический recordset добавляет поле к набору Fields главного recordset-а. Если специально не определено, ADO автоматически присваивает новому полю название “chapter”. В нем содержится указатель на тот подчиненный recordset, который связан с главным.

Иерархические recordset создаются командой SHAPE. Эта команда предоставляет два метода создания связи главный/подчиненный: Append и Compute. Для создания иерархических recordset-ов метод Append использует синтаксис SHAPE и RELATE. Этот метод, используя определенное преобразование колонок, позволяет получить связь главный-подчиненный между recordset-ами, созданными двумя разными командами.

В следующем примере демонстрируется использование иерархических recordset-ов для реализации зависимого списка. В этом случае информация о книгах, написанных определенным автором, представляется как recordset с зависимостью главный/подчиненный между авторами и названиями.

Public Function Load(ByVal AuthorId As String) As ADODB.Recordset
  Dim c As ADODB.Connection
  Dim rs As ADODB.Recordset
  Dim lsSQL As String
  'создание команды shape
  lsSQL = ""
  lsSQL = lsSQL & "SHAPE "
  lsSQL = lsSQL & " {{ call authors_BrowseAll }} "
  lsSQL = lsSQL & " APPEND "
  lsSQL = lsSQL & " ({SELECT * FROM Titles t, TitleAuthor ta "
  lsSQL = lsSQL & "  WHERE t.title_id = ta.title_id} "
  lsSQL = lsSQL & " AS titles "
  lsSQL = lsSQL & " RELATE au_id TO au_id)"
  
  Set c = New ADODB.Connection
  c.Provider = 
    "MSDataShape"c.Open"DataProvider=MSDASQL;DSN=pubs;uid=sa;pwd=;database=pubs"
  
  Set rs = New ADODB.Recordset
  rs.CursorLocation = adUseClient
  rs.Open lsSQL, c
  
  Set Load = rs
  
End Function

Заполненный recordset содержит информацию об авторе и о названиях книг, написанных этим автором. После этого полностью заполненный recordset может быть отключен и послан клиентскому компоненту одним DCOM вызовом.

ADO выполняет определенную агрегатную функцию (или несколько функций) с подчиненным recordset-ом при помощи синтаксиса COMPUTE и генерирует главный recordset. Если в оператор SHAPE включено утверждение BY, ADO автоматически создает главный recordset и добавляет дополнительное поле с названием “chapter”, содержащее подчиненные записи, содержащие агрегатные значения главного recordset-а. Если утверждение BY в оператор SHAPE не включено, ADO создает только главный recordset, а подчиненные значения не будут доступны.

Public Function BrowseAdvance() As ADODB.Recordset
  Dim rs As ADODB.Recordset
  Dim c As ADODB.Connection
  Dim lsSQL As String
  
 ‘создание команды shape
  lsSQL = “”
  lsSQL = lsSQL & “SHAPE “
  lsSQL = lsSQL & “ {{ authors_browsetitles }} “
  lsSQL = lsSQL & “ COMPUTE “
  lsSQL = lsSQL & “ (SUM(advance)) “
  lsSQL = lsSQL & “ AS chapter “
  lsSQL = lsSQL & “ BY au_id”
  
  Set c = New ADODB.Connection
  c.Provider = 
    “MSDataShape”c.Open“DataProvider=MSDASQL;DSN=pubs;uid=sa;pwd=;database=pubs”
  
  Set rs = New ADODB.Recordset
  rs.CursorLocation = adUseClient
  rs.Open lsSQL, c
  
  Set BrowseAdvance = rs
  
End Function

Recordset, формирующийся ADO, может создаваться и передаваться на клиентскую рабочую станцию в отключенном режиме. Однако команды SHAPE могут использоваться только при первоначальном создании recordset-а. Поэтому команды SHAPE не могут работать для recordset, которые уже были созданы, например, при генерации главного recordset-а (командой COMPUTE) для существующего отключенного подчиненного recordset-а.

Иерархические recordset-ы могут быть вложенными, что позволяет создавать многоуровневые связи главный/подчиненный в одном recordset-е.

Доступ к подчиненному recordset-у при работе с иерархическим recordset-ом организуется установкой соответствия между переменной объекта и полем главного recordset-а, которое представляет подчиненный recordset. Затем этот recordset будет заполнен подчиненными recordset-ами, и можно будет осуществлять навигацию по нему, как и по любому другому ADODB recordset-у. Для того, чтобы при компиляции не “смутить” Visual Basic типом данных, переменная для recordset-а должна быть объявлена как variant.

  Dim o As PubsBC.Author
  Dim lvTitle As Variant
  
  'Получение экземпляра класса Author
  Set o = New PubsBC.Author
  ‘получение информации об авторе из базы данных
  Set m_adoAuthor = o.Load(lblAuthorID.Caption)
  ‘получение подчиненного recordset — названия
  lvTitle = m_adoAuthor(“titles”)
  Set m_adoTitle = lvTitle
  ‘показать titles recordset в экранной таблице
  Set grdTitles.DataSource = m_adoTitle

В дополнение к созданию recordset-а как результата запроса к базе данных, ADO позволяет приложению программно создавать recordset-ы, не подключаясь к источнику данных. Поскольку функциональность серверного объекта Remote Data Service (RDS) встроена в ADO 2.0, создание программного recordset заметно упростилось. ADO 1.5 и RDS позволяли создавать программные recordset, для чего создавался многомерный массив вариантов, после чего последний передавался методу CreateRecordset объекта RDS Server-а. ADO 2.0 упростил эту задачу благодаря добавлению метода Append в набор Fields.

Программно создаваемый Recordset позволяет компонентам доступа к данным возвращать данные, полученные из множества источников данных в виде одного ADO recordset-а. Поскольку recordset-ы создаются и заполняются программно, recordset может быть заполнен как из OLE DB, так и не из OLE DB источников данных.

Программный recordset создается при помощи создания нового экземпляра recordset-а с последующим заполнением коллекции Fields методом Append. Перед тем, как использовать этого метод нужно, чтобы свойство CursorLocation имело значение adUseClient. Следующий код показывает использование метода Append для программного создания ADODB recordset-а, представляющего таблицу Titles (Название) из базы данных pubs.

Public Function Create() As ADODB.Recordset
  Dim adoTitles As ADODB.Recordset
  Set adoTitles = New ADODB.Recordset
  adoTitles.CursorLocation = adUseClient
  adoTitles.LockType = adLockBatchOptimistic

  ‘добавление столбцов
  With adoTitles.Fields
    .Append “title_id”, adVarChar, 6, adFldUpdatable
    .Append “title”, adVarChar, 80, adFldUpdatable
    .Append “pub_id”, adChar, 12, adFldUpdatable
    .Append “price”, adCurrency, attrib:=adFldIsNullable + adFldUpdatable
    .Append “advance”, adCurrency, attrib:=adFldIsNullable + adFldUpdatable
    .Append “royalty”, adInteger, attrib:=adFldIsNullable + adFldUpdatable
    .Append “ytd_sales”, adInteger, _
  attrib:=adFldIsNullable + adFldUpdatable
   .Append “notes”, adVarChar, 200, adFldIsNullable + adFldUpdatable
   .Append “pubdate”, adDBTimeStamp, _
  attrib:=adFldIsNullable + adFldUpdatable
  End With
  ‘перед тем, как работать с recordset , его нужно открыть
  adoTitles.Open
    adoTitles.AddNew
  Set Create = adoTitles
End Function

Помимо создания нового программного recordset-а, метод Append может использоваться для добавления дополнительных полей к существующему recordset. Эта функциональность может использоваться разработчиками приложений для получения одного recordset-а, содержащего данные из многих источников. Сначала recordset создается и заполняется из OLE DB-источника данных, затем он программно изменяется, а после этого его значения обновляются из дополнительных источников данных.

Для копирования часто используемых recordset-ов может быть использован метод Clone. Этот метод наиболее эффективен при создании и копировании recordset-ов. По умолчанию клонированный recordset синхронизируется с оригиналом. Вызов метода Requery предупредит попытку ADO синхронизировать копии при изменении в них информации.

Объект DataEnvironment и Data Environment Designer

Data Environment Designer в Visual Basic позволяет разработчикам компонентов визуально создавать и работать с ADO-объектами: connection и command. Data Environment Designer начинает с создания объекта connection. После создания connection, Data Environment Designer предоставляет графический интерфейс для связывания command-объектов с connection-объектами. Для взаимодействия с источником данных command-объекты могут использовать хранимые процедуры или SQL-запросы. Компоненты, использующие объекты DataEnvironment, видят эти команды как методы объекта DataEnvironment.

Кроме того, Data Environment Designer позволяет графически создавать иерархические command-и recordset-объекты. Например, команда BrowseById возвращает recordset, содержащий информацию об авторе (поля, перечисленные в Designer-е под командой). Одно из полей, имеющихся в recordset-е — подчиненная команда (Titles), которая возвращает recordset, содержащий информацию о книгах, написанных автором. В этом примере нами был создан главный/подчиненный recordset, идентичный созданному командой SHAPE в примере со связанными иерархическими recordset. Используя Data Environment Designer можно создавать иерархические recordset-ы без необходимости вручную писать сложный код.

DataEnvironment возвращает recordset-ы для командам, свойство Recordset Returning которых имеет значение true. Поскольку метод и свойство не могут иметь одно и то же имя, recordset-ам дается имя команды с добавлением перед ним “rs”. Так, команда BrowseById возвращает recordset с именем rsBrowseById.

При получении экземпляра объекта DataEnvironment recordset-ы закрываются. Чтобы открыть recordset, нужно выполнить команду, возвращающую соответствующий recordset. Манипулировать recordset-ом можно как до, так и после его открытия. Например, после того, как recordset был открыт один раз, поиск следующей записи можно осуществлять с помощью метода MoveNext. Все методы и свойства ADO-recordset-ов доступны из recordset-а, ассоциированного с объектом DataEnvironment.

Установки времени разработки, относящиеся к объектам connection и command, определяют свойства recordset-ов при их создании. Например, Data Environment Designer предоставляет свойства CursorLocation и LockType command-объектам, когда свойство Recordset Returning имеет значение true. Эти значения свойств будут определять соответствующие свойства recordset, который будет создаваться во время работы программы.

connection, command и recordset объекты, созданные в Data Environment Designer-е, могут быть доступны, как методы DataEnvironment. Эти объекты доступны только во время исполнения программы (для них осуществляется позднее связывание). При разработке компонентов, использующих объекты DataEnvironment, интегрированная среда разработки Visual Basic может заранее определить command-и recordset-объекты, но не их параметры. Это исключает использование именованных параметров при выполнении command-объектов.

Data Environment Designer предоставляет отличное средство времени разработки для создания сложных иерархических recordset-ов и команд, в то время, как создание объектов во время исполнения, по-прежнему остаются слишком трудоемким. Разработчики компонентов данных могут реализовать преимущества, предоставляемые возможностями графического проектировщика — Data Environment Designer, копируя сгенерированный код в свои собственные классы. Для просмотра кода, сгенерированного дизайнером, надо щелкнуть правой кнопкой мыши на управляющем объекте и выбрать опцию Hierachy Info…

Расширение поддержки потоков в Visual Basic

Как компоненты, разработанные в Visual Basic 6.0, так и предоставляемые средства поддерживают потоки (thread-safe). Это значит, что объекты Forms, UserControls и UserDocuments, разработанные в Visual Basic, теперь стали потокобезопасными. Таким образом, теперь не нужно помечать серверные компоненты для обособленного выполнения.

Поскольку ActiveX-дизайнеры, предоставляющие собой часть Visual Basic, тоже потокобезапасны, объекты DataEnvironment могут использоваться и в серверных компонентах, поддерживающих потоки.

Visual Basic по-прежнему не имеет возможности интерактивно отлаживать многопоточный компонент в многопоточной среде. Для того, чтобы это делать, компонент должен компилироваться с опцией debug и при этом нужно использовать системный отладчик Microsoft Visual Studio.

Свойство MTSTransactionMode

Система разработки Visual Basic 6.0 усиливает интеграцию с Microsoft Transaction Server (MTS), добавляя design-time свойство MTSTransactionMode для public модулей классов. У свойства MTSTransactionMode есть пять допустимых значений (NotAnMTSObject, NoTransaction, RequiresTransaction, UsesTransactions и RequiresNewTransaction), которые становятся настройками транзакций в MTS Explorer-е.

Установка свойства MTSTransactionMode при компиляции компонента помещаются в библиотеку типов (TypeLib). При инсталляции компонента в Transaction Server установка транзакции берется из TypeLib-а, что снимает необходимость для администратора устанавливать это значение в MTS Explorer.

Возможности, касающиеся разработчика клиентского компонента Windows DNA

При проектировании компонентов доступа к данным важно учитывать и то, как они повлияют на работу разработчика клиентского приложения. Чтобы понять, как будут использоваться наши компоненты доступа к данным, важно ознакомиться со средствами, доступными разработчику клиентского компонента.

Модель событий ADO и асинхронные операции

Модель событий ADO 2.0 подразделяется на два семейства событий: Connection-события и Recordset-события. События возникают перед или после выполнения определенных операций над этими объектами.

Теперь recordset-ы предоставляют разработчикам клиентского приложения возможность изменения данных на базе модели события ADO вместо ранее необходимого ручного кодирования. До ADO 2.0 разработчик клиентского приложения часто создавал Булевы флаги для того, чтобы определить, изменялись данные приложения или нет. Это зачастую приводило к ошибкам в коде в описании событий Click и KeyPress, или заставляло вручную проверять каждый элемент данных для определения наличия изменений. С помощью модели событий recordset-а в ADO 2.0, разработчик клиентского приложения может определить переменную типа recordset в модуле, используя синтаксис Dim WithEvents m_adoAuthor As ADODB.Recordset и, используя соответствующие события recordset-а, определить, была ли изменена информация в recordset-е. С помощью события WillChangeEvent разработчик клиента может определить наличие изменений в определенных полях, а затем обновить данные вызовом соответствующего бизнес-компонента. В отличие от свойства EditMode recordset-а в ADO 1.5, в ADO 2.0 recordset инициирует событие и помечает recordset как измененный, если значение данных в поле было изменено, а не просто прочитано. Кроме того, событие WillChangeField обеспечивает уведомление на уровне полей, а не на уровне записей, как свойство EditMode.

Модель событий позволяет отслеживать асинхронные операции, поддерживаемые в ADO 2.0, среди которых:

  1. Подключение к OLEDB-провайдеру
  2. Выполнение Command-объектов
  3. Получение данных-fetching

Для поддержки асинхронных операций ADO инициирует запуск соответствующих событий, которые извещают приложение о выполнении операции и о ее завершении. Например, при асинхронном получении записей от OLEDB-провайдера, будет инициировано событие FetchProgress, что позволит приложению обновлять число полученных записей. Событие FetchComplete будет инициировано, когда будет получен весь recordset либо возникнет ошибка, не позволяющая считать recordset. Windows DNA-приложения вряд ли будут использовать эти события.

Библиотека “клиентских” курсоров

Наиболее значительные расширения библиотеки клиентских курсоров с точки зрения разработчика клиентского приложения — это метод Find, а также свойства Filter и Sort. Эти методы и свойства позволяют разработчику компонента осуществлять поиск, фильтрацию и сортировку считанных recordset-ов, не обращаясь к провайдеру данных.

Метод Find и свойства Filter и Sort

Библиотека клиентских курсоров ADO 2.0 предоставляет множество методов, помогающих при манипуляциях с данными recordset-а и навигации по ним. Эти методы предоставляются recordset-ом и реализуются библиотекой клиентских курсоров. Таким образом, даже несмотря на то, что компонент доступа должен вернуть отключенный recordset по значению (by value), бизнес-компонент или презентационный компонент по-прежнему могут производить в этом recordset поиск, фильтрацию и сортировку.

Метод Find ищет в recordset запись, соответствующую указанному критерию. Если запись найдена, recordset позиционируется на ней; в противном случае он позиционируется на конце recordset. Метод Find поддерживает только простые критерии, в которых только по одному полю recordset-а.

Свойство Sort определяет одно (или более) имя поля в recordset-е, в котором должен быть изменен порядок данных. В дополнение к именам полей, может быть указан и порядок (восходящий или нисходящий). Задание свойства Sort изменяет последовательность, в которой будет осуществляться доступ к значениям recordset-а при перемещениях, но физическая последовательность recordset-а не меняется.

Свойство Filter позволяет показывать поднабор строк recordset-а, удовлетворяющих некоторым критериям. При использовании фильтрации доступными становятся только наборы строк, которые отвечают некоторому критерию. Фильтрация не удаляет наборы строк из recordset, а только влияет на то, какие наборы строк будут доступны при перемещении в recordset-ах. Присвоение фильтру значения adFilterNone делает доступными все существующие наборы строк. В критериях можно использовать следующие операторы: <, >, <=, >=, <>, =, LIKE. Критерии можно объединять с помощью логических операторов AND и OR. Также свойству Filter можно присвоить массив закладок.

Сохранение recordset-объектов

Сохранение recordset-ов позволяет вам сохранить recordset на диск вместе со всеми его данными и метаданными. Метод Save поддерживает три формата файлов: свой (внутренний) формат, Extensible Markup Language (XML) и HTML-таблицу.

После того, как recordset был записан на диск, для загрузки с диска записанного recordset и повторного его заполнения данными, которые были ранее записаны на диск, может использоваться метод Open.

Это позволяет легко пересылать recordset-ы нетрадиционными методами, такими как Microsoft Message Queue (MSMQ) сервер и по HTTP.

Расширения коснулись и метода GetString, который теперь, в дополнение к формату с ограничителями, поддерживает XML и HTML.

ADODB и ADOR

Разница между ADODB-и ADOR-recordset-ами всегда была предметом обсуждения среди разработчиков, использующих ADO как метод доступа к данным. ADODB — это полная реализация ADO API для OLE DB, которая предоставляет доступ ко всем возможностям ADO. ADOR — это более “легковесная” реализация, которая поддерживает только recordset-объекты. Для клиентских приложений, которым нужна минимальная резидентная часть, например, Web-приложений, ADOR предоставляет механизм, позволяющий клиенту понимать ADO-recordset и взаимодействовать с ним, не перегружая клиента необходимостью понимания дополнительных объектов, команд, соединений и прочего.

Подключение (binding) управляющих элементов к ADO-recordset-ам

Visual Basic 6.0 позволяет связывать управляющие элементы ActiveX с ADO-recordset-ами. Это делается с помощью ADO Data Control или напрямую recordset-ами, использующими управляющие элементы, которые поддерживают ADO-binding. Подобно тому, как можно связывать свойства класса, возможно простое или комплексное связывание с пользовательскими элементами управления. Примером управляющего элемента, поддерживающего простое связывание, являются label и текстовые окна (text boxes), а Grid является примером управляющего элемента, поддерживающего комплексное связывание.

Следует помнить, что связывание данных можно выполнять и для отключенных recordset, таким образом, компонент данных, возвращающий recordset, может быть связан с управляющим элементом ActiveX.

Следующий пример использует свойство DataSource управляющего элемента Grid для связывания с ним ADO-recordset-а. Свойства recordset будут определять установочные значения элемента управления. Например, если recordset имеет параметр read-only, то таблица тоже будет иметь этот параметр. Имена столбцов таблицы определяются именами полей в recordset-е.

  Dim objAuthor As PubsBC.Author
  Dim adoAuthors As ADODB.Recordset

  ‘получение экземпляра бизнес-компонента
  Set objAuthor = New PubsBC.Author

  ‘загрузка доступных авторов
  Set adoAuthors = objAuthor.Browse

  ‘загрузка в grid
  Set grdAuthors.DataSource = adoAuthors

  ‘размещение значения id автора в label
  Set lblAuthorID.DataSource = adoAuthors
  lblAuthorID.DataField = “au_id”

В предыдущем примере мы связали управляющий элемент Grid с отключенным ADO-recordset-ом, возвращенным бизнес-компонентом PubBC. Поскольку recordset отключен, он использует библиотеку курсоров клиента ADO, и для манипулирования с данными подключаться к серверу необходимости нет. Связывание элемента управления с recordset-ом устраняет необходимость создания кучи скучного кода, что было бы неизбежно при отображении экранной таблицы вручную.

Объекты Format

Visual Basic 6.0 предоставляет объекты Format, которые обеспечивают двустороннее преобразование данных между источником данных и связанным управляющим элементом. При чтении данных из источника данных объект Format добавляет форматирование, свойственное для связанного управляющего элемента. И наоборот, объект Format удаляет дополнительное форматирование из значения, перед тем как послать его источнику данных.

Примером может быть изменение значение флага yes/no на эквивалентное значение true/false для управляющего элемента checkbox или добавление формата даты/времени в текстовый управляющий элемент для отображения значений даты/времени.

Расширение возможностей языка

Visual Basic 6.0 поддерживает возможность создания экземпляра COM-объекта на удаленном сервере с помощью стандартной функции CreateObject. В Visual Basic 6.0 функция CreateObject может работать с дополнительным необязательным параметром, определяющим имя сервера, на котором будет создаваться экземпляр.

Пример построения компонентов доступа к данным

Теперь, после того, как мы обсудили расширения и новые возможности ADO 2.0 и Visual Basic 6.0, давайте попробуем спроектировать и создать пример бизнес-компонента. В дополнение к обсуждению базовых принципов проектирования, мы поговорим и о ряде методик программирования, которые помогут при разработке компонентов доступа к данным в Visual Basic.

Целью этих методик – дать вам отправную точку, для создания вашего первого компонента доступа к данным. Как говорилось в начале статьи нет какого-то определенного трафарета, который позволил бы создавать компоненты доступа к данным.

Используемый в примере компонент содержит два различных образца проекта — то, что может и не пригодиться в реальной работе.

Что мы собираемся создавать

Для демонстрации методик, используемых при разработке компонентов доступа к данным, мы создадим простой бизнес-компонент и клиентское приложение, обслуживающие базe данных публикаций (pubs), пример поставляемый с Microsoft SQL Server. Бизнес-компонент (PubsBC.dll) поддерживает возможность просмотра и обслуживания информации об авторах и книгах, которые они написали.

Бизнес-компонент PubsBC представляет собой очень простую модель объекта. Интерфейс компонента состоит из двух public модулей классов — Author и Title. Каждый класс предоставляет стандартный набор методов (Create, Load, Save, и Delete), который предоставляет возможность выполнять обслуживание данных класса. В дополнение к стандартным методам, каждый класс предоставляет метод Browse, который позволяет по определенному критерию получать множество экземпляров класса. Например, класс Title содержит метод BrowseByAuthor, возвращающий названия произведений, написанных определенным автором.

Сервисы данных в объектной модели представляют компоненты доступа к данным, которые обеспечивают возможность взаимодействия между бизнес-компонентами и базой данных SQL Server. С целью демонстрации методик, доступных разработчикам компонентов доступа к данным, все классы данных разработаны при помощи различных методик.

Класс данных AuthorDC разработан с помощью Visual Basic Data Environment Designer, средства графической разработки которого позволяют представлять хранимые процедуры как методы. Классы TitlesDC и TitleAuthorDC разработаны с помощью ручного кодирования. В обоих случаях в результате создается private компоненты которые и помещаются в компонент PubBC.

Что общего между обеими методиками

С помощью DataEnvironment мы можем для каждой таблицы реализовать простые хранимые процедуры, позволяющие выполнять в таблице вставку, обновление, удаление и выборку, представляя их в бизнес-классе как методы. Например, при использовании класса для реализации вставки в таблицу, код метода бизнес-объекта концентрируется на реализации бизнес-логики и бизнес-правил, связанных с добавлением автора, причем для сохранения информации об авторе не требуется кода, который напрямую взаимодействовал бы с базы данных.

При разработке middle-tier компонентов, производящих доступ к данным, важно минимизировать сетевой трафик. Это делается посылкой ADO-recordset-ов между бизнес-компонентами среднего уровня и клиентским приложением.

Разработка AuthorsDC

Объекты DataEnvironment созданы с помощью Visual Basic Data Environment Designer. Data Environment Designer поддерживает возможность ассоциации нескольких подключений (connections) с одним DataEnvironment-объектом. AuthorsDC нужно одно подключение к базе данных pubs.

Как говорилось ранее, к Visual Basic 6.0 добавлен Data Environment который обеспечивает графический и программный интерфейс доступа к сервисам баз данных. Для прозрачности и возможности повторного использования между объектами в DataEnvironment и таблицами в базе данных рекомендуется создавать связи один к одному.

После установления соединения, для реализации методов класса данных (Load, Insert, Update, Delete и Browse(s)) к DataEnvironment могут быть добавлены команды (объекты типа — command). Команды DataEnvironment один к одному соответствуют хранимым процедурам на SQL Server-е.

При работе с командами, возвращающими данные, DataEnvironment автоматически создает результирующий recordset. AuthorDC-Recordset-ы с именами rsBrowseAll и rsBrowseByID созданы для заполнения командами BrowseAll и BrowseByID.

После создания DataEnvironment-объекта, можно создать public класс Author, который будет доступен разработчикам клиентского компонента. Для получения данных обо всех авторах, PubsBC имеет метод Browse, который использует BrowseAll (объект command из DataEnvironment) и recordset rsBrowseAll. Для создания метода Browse в классе Author используется следующий метод:

Public Function Browse() As ADODB.Recordset
  ‘общий метод возвращает ADO-recordset, содержащий
  ‘информацию о авторах, используя
  ‘ DataEnvironment-объект AuthorDE

Dim deAuthor As AuthorDE

  ‘получение экземпляра окружения данных
  Set deAuthor = New AuthorDE

‘’’’’’’’’’’’’’’’’’’’’’’’’’’
  ‘Browse
‘’’’’’’’’’’’’’’’’’’’’’’’’’’

  ‘сначала для заполнения recordset-а информацией об авторе
  ‘нужно вызвать команду BrowseAll
  Call deAuthor.BrowseAll

  ‘возвращение заполненного recordset вызывающему компоненту 
  Set Browse = deAuthor.rsBrowseAll

End Function

Характеристики recordset-а rsBrowseAll определяются во время разработки свойствами, расположенными на закладке Advanced диалогового окна свойств BrowseAll. Например, для использования клиентских (client-side) курсоров следует установить в соответствующее состояние свойство Cursor Location. Эта установка необходима для создания отключенного recordset-а, который может посылаться клиенту, не нуждаясь в постоянном соединении с базой данных или бизнес-компонентом.

Сначала для сохранения внесенных в данные изменений клиентский компонент должен вызвать метод Load компонента PubsBC для получения свежих данных из БД. После этого в recordset вносятся изменения. Сохранение данных делается вызовом метода Save. Метод Save учитывает все бизнес-правила. И наконец, для записи изменений в базу данных использует команды Insert и Update объекта DataEnvironment.

Public Sub Save(Author As ADODB.Recordset)
  ‘метод позволяет клиентскому приложению производить изменения
  ‘существующей и вставлять новую информацию об авторе в БД
   
   Dim deAuthor As AuthorDE
   ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’

  ‘Бизнес-правила
   ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’

   ‘отсутствуют
   ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’

   ‘Запись…
   ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’

   ‘получение экземпляра DataEnvironment-объекта
    Set deAuthor = New AuthorDE

    ‘определение того, что выполняется — вставка или обновление
    If IsEmpty(Author(“au_id”).OriginalValue) Then
     ‘выполняется вставка
     With deAuthor.Commands(“Insert”)
         Parameters(“au_id”).Value = Author(“au_id”).Value
         Parameters(“au_lname”).Value = Author(“au_lname”).Value
         Parameters(“au_fname”).Value = Author(“au_fname”).Value
         Parameters(“phone”).Value = Author(“phone”).Value
         Parameters(“address”).Value = Author(“address”).Value
         Parameters(“city”).Value = Author(“city”).Value
         Parameters(“state”).Value = Author(“state”).Value
         Parameters(“zip”).Value = Author(“zip”).Value
         Parameters(“contract”).Value = Author(“contract”).Value
        .Execute
      End With
    Else

       ‘выполняется обновление
       With deAuthor.Commands(“Update”)
         Parameters(“au_id”).Value = Author(“au_id”).Value
         Parameters(“au_lname”).Value = Author(“au_lname”).Value
         Parameters(“au_fname”).Value = Author(“au_fname”).Value
         Parameters(“phone”).Value = Author(“phone”).Value
         Parameters(“address”).Value = Author(“address”).Value
         Parameters(“city”).Value = Author(“city”).Value
         Parameters(“state”).Value = Author(“state”).Value
         Parameters(“zip”).Value = Author(“zip”).Value
         Parameters(“contract”).Value = Author(“contract”).Value
        .Execute
      End With
    End If

End Sub

Это по-прежнему сложный путь реализации классов данных, поскольку для доступа к разным типам управляющих (command) объектов необходим различный синтаксис. Например, для использования команд DataEnvironment, возвращающих данные (например метод Browse), команда вызывается перед использованием recordset-а. Команды, не возвращающие данные, могут вызываться просто также как методы любого другого объекта.

Как говорилось ранее, Data Environment Designer используется для графического создания сложных command-Zи recordset-объектов. Код, сгенерированный Data Environment Designerом-, необходимо скопировать в ваши собственные классы, обрабатывающие данных. Этот метод во многих случаях предоставляет более высокое быстродействие и облегчает разработку бизнес-компонентов, сохраняя одинаковый синтаксис для всех объектов command. DataEnvironment-объектам нужен один синтаксис для работы с командами, возвращающими recordset и другой — для команд, обновляющих данные.

Разработка TitlesDC

Класс TitlesDC — это пример класса, разработанного вручную. Разработка этого механизма выглядит скучной, но легко может быть автоматизирована. Можно создать средство генерации пользовательского кода, которое вместо вас будет выполнять всю монотонную работу по вашему шаблону.

Как и AuthorsDC, TitlesDC — это private класс, содержащийся в PubsBC и обеспечивающий механизм доступа к данным между таблицей Titles в SQL Server и классом Title. В этом случае стандартные команды базы данных реализуются как public методы класса TitlesDC. Для взаимодействия с базой данных по-прежнему используются хранимые процедуры.

Метод TitlesDC.BrowsebyAuthor позволяет бизнес-компоненту получать доступ к информации о произведениях указанного автора.

Public Function BrowseByAuthor(ByVal au_id As Variant) As ADODB.Recordset
‘*** GENERATED BY HUEY v2.1, THE 3-TIER DATA TOOL KIT ***
‘*** (C) 1997 CLARITY CONSULTING, INC. ***
On Error GoTo Err
  Dim adoTitles As ADODB.Recordset
  Dim adoCmd As ADODB.Command
  Dim adoParam As ADODB.Parameter
  Set adoCmd = New ADODB.Command
  With adoCmd
    .CommandType = adCmdStoredProc
    .CommandText = “titles_BrowseByAuthor”
    ‘Создание набора параметров
     Set adoParam = .CreateParameter(Name:=”RETURN_VALUE”, _
                      Type:=adInteger, _
                      Direction:=adParamReturnValue)
   .Parameters.Append adoParam
   Set adoParam = .CreateParameter(Name:=”@au_id”, _
                      Type:=adVarChar, _
                      Direction:=adParamInput, _
                      Size:=11, _
                      Value:=au_id)
    .Parameters.Append adoParam
  End With
  Set adoTitles = New ADODB.Recordset
  adoTitles.CursorLocation = adUseClient
  adoCmd.ActiveConnection = GetConnection()
  Call adoTitles.Open(Source:=adoCmd)
  Set adoTitles.ActiveConnection = Nothing
  Set BrowseByAuthor = adoTitles
  Exit Function
  Err:
  Call RaiseErr
End Function

Для более высокого быстродействия список параметров command-объекта создается программно. Это предупреждает появление лишних сетевых обменов. Если список параметров не заполнен вручную, то при доступе к первому элементу списка ADO автоматически заполнит этот список, обратясь, для этого, к серверу баз данных. Обычно во время разработки информация о параметре разработчику известна, так что программное создание списка параметров уменьшит сетевой трафик, что естественно увеличит быстродействие.

Для предоставления информации клиентскому компоненту класс Title бизнес-компонента реализует метод BrowseByAuthor, использующий private метод TitlesDC.BrowseByAuthor.

Public Function BrowseByAuthor(ByVal AuthorId As String) As ADODB.Recordset
   ‘возвращает названия произведений указанного автора
   Dim objTitle As TitleDC

   ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’
   ‘Browse
   ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’

   ‘получение экземпляра класса данных
  Set objTitle = New TitleDC
  Set BrowseByAuthor = objTitle.BrowseByAuthor(AuthorId)

End Function

Обновление данных еще более просто. Поскольку бизнес-компоненты и классы взаимодействуют через ADO-recordset-ы, безобразный код переназначения полей recordset-а на параметры command-объектов может быть инкапсулирован в методе класса обработки данных.

Например, метод TitlesDC.Update, записывающий изменения информации о названиях произведений, вручную создает и заполняет список параметров для хранимой процедуры. Значения параметров берутся из recordset-а “titles” который передается в этот метода в качестве параметра.

Public Function Update(titles As ADODB.Recordset) As Long
'*** GENERATED BY HUEY v2.1, THE 3-TIER DATA TOOL KIT
'*** (C) 1997 CLARITY CONSULTING, INC. ***
 On Error GoTo Err
  Dim adoCmd As ADODB.Command
  Dim adoParam As ADODB.Parameter
  Set adoCmd = New ADODB.Command
  With adoCmd
    .CommandType = adCmdStoredProc
    .CommandText = "titles_update"
    'Создание набора параметров
    Set adoParam = .CreateParameter(Name:="RETURN_VALUE", _
                    Type:=adInteger, _
                    Direction:=adParamReturnValue)
    .Parameters.Append adoParam
    Set adoParam = .CreateParameter(Name:="@title_id", _
                    Type:=adVarChar, _
                    Size:=6, _
                    Direction:=adParamInput, _
                    Value:=titles("title_id").Value)
    .Parameters.Append adoParam
    Set adoParam = .CreateParameter(Name:="@title", _
                    Type:=adVarChar, _
                    Size:=80, _
                    Direction:=adParamInput, _
                    Value:=titles("title").Value)
    .Parameters.Append adoParam
    Set adoParam = .CreateParameter(Name:="@type", _
                    Type:=adChar, _
                    Size:=12, _
                    Direction:=adParamInput, _
                    Value:=titles("type").Value)
    .Parameters.Append adoParam
    Set adoParam = .CreateParameter(Name:="@pub_id", _
                    Type:=adChar, _
                    Size:=4, _
                    Direction:=adParamInput, _
                    Value:=titles("pub_id").Value)
    .Parameters.Append adoParam
    Set adoParam = .CreateParameter(Name:="@price", _
                    Type:=adCurrency, _
                    Size:=8, _
                    Direction:=adParamInput, _
                    Value:=titles("price").Value)
    .Parameters.Append adoParam
    Set adoParam = .CreateParameter(Name:="@advance", _
                    Type:=adCurrency, _
                    Size:=8, _
                    Direction:=adParamInput, _
                    Value:=titles("advance").Value)
    .Parameters.Append adoParam
    Set adoParam = .CreateParameter(Name:="@royalty", _
                    Type:=adInteger, _
                    Size:=4, _
                    Direction:=adParamInput, _
                    Value:=titles("royalty").Value)
    .Parameters.Append adoParam
    Set adoParam = .CreateParameter(Name:="@ytd_sales", _
                    Type:=adInteger, _
                    Size:=4, _
                    Direction:=adParamInput, _
                    Value:=titles("ytd_sales").Value)
    .Parameters.Append adoParam
    Set adoParam = .CreateParameter(Name:="@notes", _
                    Type:=adVarChar, _
                    Size:=200, _
                    Direction:=adParamInput, _
                    Value:=titles("notes").Value)
    .Parameters.Append adoParam
    Set adoParam = .CreateParameter(Name:="@pubdate", _
                    Type:=adDBTimeStamp, _
                    Size:=8, _
                    Direction:=adParamInput, _
                    Value:=titles("pubdate").Value)
    .Parameters.Append adoParam
    .ActiveConnection = GetConnection()
    .Execute
  End With
  Update = adoCmd.Parameters("RETURN_VALUE").Value
  Exit Function
Err:
  Call RaiseErr
End Function
Public класс Title предоставляет метод Save, который реализует бизнес-правила, связанные с 
изменением информации о названиях, а для внесения изменений в базу данных использует 
метод TitlesDC.Update.
Public Sub Save(Title As ADODB.Recordset)
  ‘сохранение изменения информации в recordset Title
  Dim objTitle As TitleDC
  Dim lRtn As Long
  ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’

  ‘Бизнес-правила
  ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’

  ‘Отсутствуют
  ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’

  ‘Запись…
  ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’

   ‘получение экземпляра класса title
  Set objTitle = New TitleDC

  ‘определение того, что делается — вставка или обновление
  If IsEmpty(Title(“title_id”).OriginalValue) Then
   lRtn = objTitle.Insert(Title)
  Else
   lRtn = objTitle.Update(Title)
  End If

  ‘обновление recordset-а
  Title.UpdateBatch

  ‘готово
End Sub

 Этот механизм предоставляет прозрачный интерфейс между public методами бизнес-компонентов и private методами компонентов обработки данных. Разработчики бизнес-компонентов не должны заботиться о коде доступа к данным.

Поскольку код доступа к данным может быть сгенерирован по схеме базы данных, для создания кода доступа к данным в Visual Basic можно создать простой генератор кода.

В больших приложениях для множества бизнес-компонентов необходимо иметь доступ к одним и тем же классам обработки данных. Это можно обеспечить созданием public, взамен private классов обработки данных. При возникающих изменениях схемы базы данных изменениям подвержены только public классы обработки данных, и в целом бизнес-компоненты влияния этих изменений на себе не испытывают.

Дополнительная информация

Microsoft ActiveX Data Objects (ADO) documentation в Microsoft Data Access SDK документации.
Microsoft Visual Basic documentation в Visual Studio 6.0 MSDN™ Library Online и CD.v


Copyright © 1994-2016 ООО "К-Пресс"