Избранные вопросы программирования объектов доступа к данным. Примеры интеграции ADO с Microsoft SQL Server на VB и Delphi.

Содержание статьи

      Достоинства и недостатки новых компонентов доступа к данным.

Как правильно инициализировать DataEnvironment?

Пример использования источника данных из DataEnvironment в программе.

Пример использования объекта ADODB.

ADO или BDE? Компромиссное решение проблемы.

Пример использования ADO в программе на Delphi.

Ограничения ADO в Delphi.

Заключение.

К вопросу об установке Microsoft Visual Studio 98.

Во-первых избегайте использования beta-версии продукта, т.к. при переходе на коммерческую версию необходимо будет переформатировать диск C: (!!!), об этом сказано и в readme файле, но в виде небольшого примечания.

Во-вторых не устанавливайте английскую версию Visual Studio поверх русской, т.к. деинсталляция продукта не вычищает полностью системный каталог Windows, в результате чего часть компонентов остается русскоязычными и ваша система будет представлять «гибрид» русской и английской версий. Если вы пользуетесь Microsoft Office 97, то будьте готовы к тому, что VS 98 заменит некоторые общие библиотеки (mso97rt.dll и др.), но серьезных неприятностей это не сулит. При использовании MS Office 2000 проблемы не существует вовсе.

Если вы ранее пользовались VB 5.0, то при переходе на VB 6.0 возникнут проблемы при перекомпиляции проектов, использующих компонент DataGrid (DBGrid32.OCX) из-за несоответствия CLSID, однако этот момент можно легко исправить, покопавшись в исходниках своей программы. Также возможны проблемы при компиляции проектов, использовавших первые версии ADODC по той же самой причине.

В начало страницы

Достоинства и недостатки новых компонентов доступа к данным.

В Visual Studio 98 появились новые возможности по использованию удаленных источников данных, в первую очередь это — Data Environment, включающий в себя визуальное управление объектами типа Connection и Command, с возможностью подключения к СУБД без использования DSN файлов, а также визуальным конструктором запросов, как в MS Access. Запросы, созданные в Data Environment можно использовать в программе как объекты типа Recordset с одной стороны и как независимые команды SQL с другой, ниже это будет показано на примере.

Все сказанное выше, конечно повышает скорость создания приложений и удобство работы, однако сильно радоваться не стоит. Если вы используете Visual Basic, то некоторые свойства, прописанные на стадии конструирования (design-time) в Data Envirionment, вам не удастся изменить во время выполнения программы (run-time) имеется в виду начальный запуск, это относится прежде всего к строке подключения (login & password), т.к. их переопределение возможно уже после того, как Data Environment инициализирован на уровне входа в соответствующую DLL и отработала строка подключения, созданная в design-time. Такое поведение программы заложено компилятором, но вы можете его и не обнаружить, если работаете с SQL СУБД на правах администратора, только при установке программы заказчику, эта неприятность может проявить себя.

Еще один «подводный камень» таится в использовании визуального компонента ADODC. Если вы захотите присоединить к ADODC локальную базу данных (обычный *.MDB файл), то заметите, что свойство Connection String во время выполнения программы доступно для переопределения, когда объект уже инициализирован и имеет возможность открыть базу данных прописанную в design-time. Еще большее неудобство приносит то, что Connection String не может быть задан пустой строкой. Однако выход из ситуации существует и заключается во временном копировании открываемого файла в заведомо существующий каталог (можно и сетевой) перед инициализацией формы, использующей ADODC, а затем переопределением Connection String на рабочий путь программы и удалением копии локальной базы, на который ADODC настроен в design-time. При использовании подключения ADODC к SQL СУБД, проблема аналогична ситуации с Data Environment и может быть разрешена дополнительным вводом login-a и пароля при запуске приложения, однако если подключение происходит к нескольким базам данных, то здесь есть над чем поработать!

Если у вас достаточно времени, чтобы работать над проектом, то я рекомендую не злоупотреблять использованием Data Environment, а работать с объектами ADODB.Connection, ADODB.Command, ADODB.Recordset и прочими ADO (ActiveX™ Data Objects). Конечный результат в итоге окажется более надежным и мобильным.

В начало страницы

Как правильно инициализировать Data Environment?

В принципе сразу после создания внутри проекта Data Environment и заполнения его объектами Connection и Command, он становится доступным во время выполнения программы, однако для придания программе «независимости» необходимо в процедуре обработки события Data Environment Initialize поместить код инициализации, например:

Private Sub DataEnvironment_Initialize()

Dim ConnectTo As String

ConnectTo = BuildConnection(DBname:="DB_NSI")

DEC1.Open ConnectTo, ODBC.UserName, ODBC.Password

ConnectTo = BuildConnection(DBname:="DB_REPORTS")

DEC2.Open ConnectTo, ODBC.UserName, ODBC.Password

DECJET.ConnectionString = _

"Provider=Microsoft.Jet.OLEDB.3.51;Persist Security Info=False;Data Source=" & _

App.Path & DBname

End Sub

Этот код будет храниться непосредственно в файле *.dsr вместе с настройками Data Environment. Как видно из примера внутри Data Environment открываются три объекта Connection. DEC1 и DEC2 – для подключения к базе данных на SQL Server и DECJET – для подключения к локальной базе данных (\Mydb.MDB прописано в переменной DBName). Функция BuildConnection просто возвращает строку подключения к конкретной базе данных, например такую: «Provider=MSDASQL.1; Password=mypass; Persist Security Info=True; User ID=Alex; Extended Properties= "DRIVER= SQL Server; SERVER=SQL_SERVER; UID=Alex; APP= Microsoft® VS 98; WSID= ARM_ADMIN; DATABASE= DB_NSI"; Initial Catalog= DB_NSI». Еще несколько слов скажу об объекте ODBC – он инкапсулирует функции Win32 API, позволяющие снять имя пользователя и компьютера. Свойство Password нужно задать после инициализации ODBC, предварительно запросив его у пользователя. Кроме того объект ODBC имеет еще несколько полезных функций, их смысл будет понятен, если взглянуть на исходник этого класса – ODBC_SQL.CLS (9191). Если вам лень копаться в исходнике, то предлагаю откомпилированный вариант этого объекта – файл ODBC_SQL.DLL (40960). Его свойства и методы можно посмотреть через object browser в VB или соответствующий Unit в Delphi – DAO_ODBC.PAS (6703). Инициализировать экземпляр объекта ODBC желательно используя модель раннего связывания и соответствующая объектная переменная должна быть глобальной в проекте, т.к. в процессе работы программы переопределение свойств ODBC как правило не требуется.

В начало страницы

Пример использования источника данных из Data Environment в программе.

Итак, для того, чтобы использовать объект Data Environment в программе существует как минимум два варианта.

1.Можно выполнять команды Data Environment и результирующий набор данных использовать как обычный Recordset:

Private Sub Updatedbp()

Dim l_rs As Recordset, g_rs As Recordset, Up As Boolean

With DE1

If .Recordsets("cmdUp").State = 0 Then .cmdUp

If .Recordsets("cmdLink").State = 0 Then .cmdLink

If .Recordsets("cmdUp").BOF Then Exit Sub

Set g_rs = .Recordsets("cmdLink").Clone

g_rs.Filter = "dataop = #" & Format(DTF, SqlDateFormat) & "# AND kod_pok = '" & PL & "' "

.Recordsets("cmdUp").MoveFirst

Up = False

Do While Not .Recordsets("cmdUp").EOF

If g_rs.BOF Then

g_rs.AddNew

g_rs!dataop = DTF

g_rs!mfo = .Recordsets("cmdUp")!Kod_mfo

g_rs!Kod_pok = PL

g_rs!zn = .Recordsets("cmdUp")!Sum

g_rs.Update

DoEvents

Else

g_rs.MoveFirst

Up = True

Do While Not g_rs.EOF

If g_rs!dataop = DTF And g_rs!mfo = .Recordsets("cmdUp")!Kod_mfo _

And g_rs!Kod_pok = PL Then

Up = False

g_rs!zn = .Recordsets("cmdUp")!Sum

g_rs.Update

DoEvents

End If

g_rs.MoveNext

Loop

If Up Then

g_rs.AddNew

g_rs!dataop = DTF

g_rs!mfo = .Recordsets("cmdUp")!Kod_mfo

g_rs!Kod_pok = PL

g_rs!zn = .Recordsets("cmdUp")!Sum

g_rs.Update

DoEvents

End If

End If

.Recordsets("cmdUp").MoveNext

Loop

g_rs.Close

DoEvents

If .Recordsets("cmdUp").State <> 0 Then .rscmdUp.Close

If .Recordsets("cmdLink").State <> 0 Then .rscmdLink.Close

End With

DoEvents

End Sub

В этом примере внутри объекта Data Environment с именем DE1 открываются наборы данных, путем выполнения соответствующих команд (cmdUp, cmdLink). Причем имена этих команд являются методами DE1, а соответствующие наборы данных имеют те же имена, но с приставкой «rs» (rscmdUp, rscmdLink) и являются свойствами DE1 типа Recordset. На эти же свойства ссылаются элементы коллекции DE1.Recordsets. Свойство State для каждого элемента коллекции DE1.Recordsets показывает была ли выполнена соответствующая команда DE1 (State=0 означает, что набор данных не был открыт). Операции по проверке состояния Recordset полностью лежат на программисте, т.к. попытка закрыть закрытый или открыть открытый набор данных выбросит исключение. После того, как набор данных открыт, его можно использовать в программе классическим способом, как в VBA.

2. Можно выполнять команду Data Environment и результат сразу записывать в базу данных. Это относится прежде всего к таким объектам Command, у которых значение свойства Command Text содержит SQL оператор для удаления («DELETE … FROM …») или обновления («UPDATE … SET …») записей базы.

Реализация этого приема работы не требует особых навыков программирования и похожа на выполнение обычной хранимой процедуры:

DE1.Commands("cmdName").Execute или

DE1.Commands(1).Execute‘ если известен индекс выполняемой команды в коллекции Commands. Результат выполнения метода Execute также может быть присвоен переменной типа Recordset и использоваться далее, однако не следует путать набор данных типа DAO.Recordset и Recordset, возвращаемый из Data Environment. Несоответствие типов может привести к ошибке времени выполнения (run-time error). Подробное описание свойств и методов объектов Command и Connection есть в документации MSDN.

В начало страницы

Пример использования объекта ADODB.

Среди объектов доступа к данным, входящих в Visual Studio 98, есть компонент не имеющий интерфейса настройки во время создания проекта, и более эффективный, чем Data Environment, это – ADODB или, если говорить точнее, Microsoft ActiveX™ Data Objects 1.0. Использование данного компонента также основано на создании экземпляров объектов типа ADODB.Connection, ADODB.Command, ADODB.Recordset, ADODB.Field. Следующий пример демонстрирует заполнение элемента управления ListBox с именем lstr, значениями набора данных с SQL СУБД, используя ADODB:

Private Sub FillList()

Dim cnn As ADODB.Connection

Dim rst As ADODB.Recordset

Dim cmd As ADODB.Command

Dim dat As String, i As Integer

Set cnn = New ADODB.Connection

cnn.Open BuildConnection("DB_REPORTS")

Set rst = New ADODB.Recordset

Set cmd = New ADODB.Command

Set cmd.ActiveConnection = cnn

dat = Format(cbDate.Text, "mm-dd-yy")

cmd.CommandText = "SELECT kod_otdel AS kod_otdel, kod_otc AS kod_otc, vid_otc AS vid_otc, razr AS razr "

cmd.CommandText = cmd.CommandText & "From dbo.v_reports1999 WHERE dataotc = '" & dat & "' GROUP BY kod_otdel, "

cmd.CommandText = cmd.CommandText & "kod_otc, vid_otc, razr ORDER BY kod_otdel, kod_otc "

Set rst = cmd.Execute

lstr.ListItems.Clear

If Not rst.BOF Then

rst.MoveFirst

i = 1

While Not rst.EOF

With lstr

.ListItems.Add , , rst!kod_otdel

.ListItems(i).ListSubItems.Add , , rst!kod_otc

.ListItems(i).ListSubItems.Add , , rst!vid_otc

.ListItems(i).ListSubItems.Add , , rst!razr

.ListItems(i).Bold = True

rst.MoveNext

i = i + 1

End With

Wend

End If

rst.Close

cnn.Close

Set rst = Nothing

Set cnn = Nothing

End Sub

Присвоение значения Nothing, объектным переменным после их использования в данном случае чисто формальное, т.к. при выходе из процедуры память освобождается и все локальные переменные уничтожаются, однако в более сложной ситуации, когда необходимо инициализировать ADODB в цикле, всегда следует освобождать объектные переменные во избежание утечки памяти. Функция BuildConnection здесь, как и в одном из предыдущих примеров возвращает строку подключения к конкретной БД.

В основу эффективной работы ADODB (и других ADO) положено прямое взаимодействие с низкоуровневыми интерфейсами OLE DB, который в свою очередь использует инфраструктуру модели OLE COM (Component Object Model), что сокращает ненужное дублирование сервисов и расширяет возможности взаимодействия не только для разных источников информации, но и для разных программных сред и инструментов. OLE DB — это способ доступа к данным в среде COM. Небольшое руководство по работе непосредственно с OLE DB можно найти в файле Ole4Odbc.doc (301 568) на английском языке.

В начало страницы

ADO или BDE? Компромиссное решение проблемы.

Тем, кто пишет программы для работы с СУБД на Delphi, хорошо знакомы компоненты, взаимодействующие с BDE (Borland Database Engine), а также сам BDE. Но возникает вопрос, можно ли использовать в Delphi проектах другие объекты доступа к данным. Ответ — да, можно, однако здесь есть некоторые тонкости, на которых мы и остановимся далее.

Общеизвестно, что при разработке приложений под Win32, независимо от языка программирования можно использовать практически все доступные в системе компоненты, если они корректно зарегистрированы (установлены). Предположим, что мы разрабатываем приложение, осуществляющее выборку/обновление данных в БД на SQL Server, но не хотим привязываться к BDE. В таком случае у нас есть выбор: это либо ADO, либо ODBC Direct объекты. Использование ODBC Direct с удаленной СУБД несколько ограничено (тем, кто писал на VBA это знакомо) и требует привязки через DSN файлы, или драйверы ODBC, т.е. технология практически идентичная BDE, только более примитивная. Использование ADO — работа с обычной ActiveX DLL, поэтому сначала необходимо получить файл, содержащий декларации всех объектов. Этот файл ADODB_TLB.PAS (46980 для ADO версии 1.0) можно сгенерировать при помощи самой Delphi. Выберите в меню пункт Project/Import Type Library… и, если в системе установлены ADO, то в списке объектов найдите пункт Microsoft ActiveX Data Objects 2.1 Library (версия библиотеки может быть и другая) после чего, щелкнув на OK к вашему проекту добавится модуль, содержащий все необходимые определения. В модуле, объекты доступа к данным описаны следующим образом:

CoRecordset = class

class function Create: _Recordset;

class function CreateRemote(const MachineName: string): _Recordset;

end;

CoConnection = class

class function Create: _Connection;

class function CreateRemote(const MachineName: string): _Connection;

end;

CoCommand = class

class function Create: _Command;

class function CreateRemote(const MachineName: string): _Command;

end;

CoParameter = class

class function Create: _Parameter;

class function CreateRemote(const MachineName: string): _Parameter;

end;

Кроме того, в виде перечислений реализованы все используемые константы. Из определений следует, что можно напрямую создавать экземпляры классов Recordset, Connection, Command, Parameter. Остальные определения относятся непосредственно к инкапсулированным интерфейсам OLE DB и объектам, входящим в указанные классы. Для использования ADO в программе необходимо описать переменные соответствующих типов (см. определения в файле), например:

var

Cnn: Connection;

Rst: Recordset;

Cmd: Command;

Prm: Parameter;

Далее в теле процедуры или функции создаем экземпляры классов, например:

Cnn:=CoConnection.Create;

Rst:=CoRecordset.Create;

После этого можно непосредственно работать со свойствами и методами ADO. Рекомендуется после создания экземпляров классов проверить значение объектной переменной на nil, а еще лучше заключить конструкцию в блок try… except… end, т.к. при неуспешном создании экземпляра класса будет выброшено исключение EOleException.

Следует также отметить, что созданные экземпляры классов не имеют деструктора Free или Destroy и не наследуют из других классов (см. определения), поэтому освобождать память, занятую экземпляром класса после использования не нужно. В процедурах и функциях при выходе такие объектные переменные будут самостоятельно освобождены. Использование Dispose в такой ситуации может привести к серьезной ошибке!

Еще несколько слов следует сказать по поводу конструктора CreateRemote, он позволяет создать экземпляр класса, который работает на удаленном компьютере. На машине-клиенте в таком случае требуется *.TLB библиотека удаленного сервера автоматизации, а также компоненты DCOM, в случае, если клиент работает под Windows 95/98. При использовании Windows NT или Windows 2000 на клиенте требуется только правильная регистрация *.TLB, а связь с серверным приложением будет осуществляться посредством Automation Manager. Построение серверов автоматизации ActiveX выходит за рамки данной статьи и для детального изучения этой задачи следует обратиться к литературе (ActiveX_Srv.doc 167 424).

В начало страницы

Пример использования ADO в программе на Delphi.

На примере использования объекта ADODB.Connection покажем как можно записывать данные в таблицы на SQL Server, а также обновлять их. Пример взят из реального приложения, которое осуществляет одну из стадий процесса ежедневной загрузки оперативной информации в БД Минского областного управления Белагропромбанка. Важные строки кода выделены красным цветом. Не стоит обращать внимание на множество вспомогательных переменных и функций, здесь важно понять принцип:

function TfrmMain.ProcessData: integer;

var

i, j, k, c, intables, temp, total: integer;

Connect, Database, TableName, Password, SQL: string;

OtherTables: array [1..5] of string;

Cnn: Connection;

LV: OleVariant;

CancelUpdate, F: boolean;

begin

StillExecuting:=True;

Result:=-1;

if LoadDataToMemory then begin

total:=GiveTotalProgress;

c:=0;

{Главный цикл записи в базу на SQL Server

Здесь записываются данные только в основные таблицы}

for i:=1 to Files.Count do begin

Application.ProcessMessages;

F:=false;

CancelUpdate:=false;

Database:=GetSQLDatabase(TOFile[i].Name, 1, TableName, intables);

{intables - содержит общее количество таблиц для записи

в данной базе Database. 1- индекс 1-й таблицы, которая присутствует всегда!}

OtherTables[1]:=TableName;

if intables>1 then

for k:=2 to intables do

GetSQLDatabase(TOFile[i].Name, k, OtherTables[k], temp);

Password:=ReadFromINI(AppINIFile, 'Source', 'OFilePwd');

Caption:= InternalFile+' Updating SQL DB ('+Database+

') table ('+TableName+')...';

Connect:=BuildConnection(Database, Password);

try

Cnn:=CoConnection.Create;

if Cnn<>nil then begin

try

Cnn.Open (Connect, PrivateUserName, Password);

Cnn.BeginTrans;

for j:=1 to TOFile[i].TotalRecords do begin

if TOFile[i].Name<>'Saldo' then begin

SQL:=GetInsertSQLStatement(TableName, TOFile[i].Name,

TOFile[i].TotalColumns, j);

inc(c);

if ((TOFile[i].Name='Sceta') and (TOFile[i].Matrix[j][2]='')) then

CancelUpdate:=true else CancelUpdate:=false;

end

else begin

{Здесь обработка сальдо - 3 таблицы}

inc(c);

for k:=1 to intables do begin

CancelUpdate:=true;

SQL:=GetInsertSQLStatement(OtherTables[k], TOFile[i].Name,

TOFile[i].TotalColumns, j);

if k<>1 then begin

case k of

2: F:=do_saldo_kre(j);

3: F:=do_saldo_vne(j);

end;

if F then Cnn.Execute(SQL, LV, adCmdText);

end

else if IsMonthChanged then Cnn.Execute(SQL, LV, adCmdText);

end;

end;

if not CancelUpdate then Cnn.Execute(SQL, LV, adCmdText);

SetProgress(20, 80, c, total);

Application.ProcessMessages;

end;

Cnn.CommitTrans;

except

on EOleException do Cnn.RollbackTrans;

HandleExceptions;

end;

Cnn.Close;

end;

Result:=0;

except

Result:=1;

end;

end;

end;

end;

Работа функции начинается с определения имени базы данных (переменная Database) в которую предполагается записывать данные, имени компьютера-клиента (функция PrivateUserName) и пароля (переменная Password) для доступа к БД. Далее функция BuildConnection возвращает строку подключения к СУБД SQL Server в переменную Connect. Следует отметить, что строка подключения в данном случае отличается от той, которая используется в аналогичной ситуации в программе на VB. В данном случае строка подключения содержит следующее:  driver={SQL Server}; server=SQL_Server; uid=Alex; pwd=mypass; database=any_db; как видно из записи, в строке подключения отсутствует ссылка на Provider; значения для pwd и database естественно берутся из передаваемых параметров.

Далее создается экземпляр объекта Connection cnn:=CoConnection.Create) и в случае успеха открывается сеанс работы с одной из таблиц БД (Cnn.Open). Для повышения сохранности данных в процессе записи в БД используются методы для работы с транзакциями BeginTrans, CommitTrans и RollBackTrans. В случае, если одна из попыток записи в таблицу приводит к исключению, RollBackTrans (см. блок except) позволяет откатить транзакции назад и вернуть прежнее состояние данных в таблице. Фактическая запись данных в БД происходит только когда выполнится метод CommitTrans, а не Execute; Все вызовы Execute приводят лишь к передаче данных на SQL Server и помещению их во временные таблицы системной базы tempdb, которая управляется самой СУБД. Администратору базы данных следует внимательно следить за тем, чтобы не переполнился журнал транзакций для рабочей БД, в противном случае у клиента также возникнет исключение при попытке выполнить очередной Execute.

Цикл записи в таблицу БД состоит из последовательного выполнения инструкций INSERT INTO … VALUES …. Каждый оператор SQL конструируется на основе данных, хранящихся в специальной структуре в памяти, имеющей вид прообраза таблицы в БД и при помощи функции GetInsertSQLStatement присваивается переменной SQL, затем выполняется Execute с данным параметром, а в качестве типа выполняемой команды передается константа adCmdText (при выполнении хранимых процедур передается adCmdStoredProc). Синтаксис оператора INSERT … INTO подробно описан в документации по MS SQL и здесь приводиться не будет. Следует отметить, что выполнять можно любую конструкцию SQL, это относится и к расширенному INSERT … INTO … SELECT, и к инструкциям UPDATE … SET, DELETE … FROM, SELECT … FROM. В случае с выборкой данных (SELECT ... FROM) необходимо позаботиться о том, чтобы результирующий набор данных был передан в объект Recordset, поэтому выполнение такой инструкции целесообразно при открытии Recordset как динамического набора данных, либо при использовании результирующего значения метода Connection.Execute (см. описание).

Что касается вызова хранимой процедуры при помощи метода Connection.Execute, то в качестве параметров передается имя хранимой процедуры с аргументами, переменная типа OleVariant и константа adCmdStoredProc:

Cnn.Execute(SPName, _V, adCmdStoredProc);

Аргументы хранимой процедуры следует заключать в скобки, например “ProcedureName (arg1, arg2, …, argN)”. Если хранимая процедура возвращает результирующий набор данных, то результат выполнения метода Execute нужно присвоить объектной переменной типа Recordset.

В начало страницы

Ограничения ADO в Delphi.

Рассмотрим некоторые принципиальные ограничения при использовании объектов ADO в Delphi проектах. В основе этих ограничений в первую очередь лежит невозможность вызова одних и тех же методов разным способом (с параметрами и без параметров, а также с ограниченным числом параметров). Это обусловлено тем, что в Delphi все внешние вызовы декларируются однозначно и более того, нельзя использовать Optional (необязательные) аргументы. Даже если функция может принять все аргументы как Optional, в Delphi обязательно должны быть переданы конкретные значения, как правило переменные типа OleVariant. Это ограничение принципиально и связано с тем, что ADO объекты ориентированы прежде всего на использование в VisualBasic и VBScript проектах, где передача Optional параметров является обычной конструкцией языка. Следует отметить, что передача в качестве Optional параметра значения nil приводит к ошибке несоответсвия типов в случае использования ADO, хотя в некоторых ActiveX™ объектах это допускается. Для пояснения ситуации рассмотрим примеры декларации некоторых методов объекта Recordset:

procedure AddNew(FieldList: OleVariant; Values: OleVariant); safecall;

procedure Update(Fields: OleVariant; Values: OleVariant); safecall;

Из описания видно, что использование AddNew возможно не иначе, как с параметрами FieldList и Values, что практически сводит на нет добавление записей в Recordset классическим способом: сначала AddNew без параметров, затем обработка коллекцииFields и после этого Update также без параметров. Если же попробовать описать метод AddNew повторно, как procedureAddNewsafecall; то это приведет к ошибке компиляции несмотря на то, что формально такое описание возможно, т.к. все аргументы AddNew являются Optional. Это же относится и ко всем аналогичным методам объектов ConnectionCommandParameter и Recordset.

Одним из способов решения такой проблемы, является инкапсуляция методов, использующих Optional параметры внутрь ActiveXDLL, написанной на VB и содержащей все необходимые Published методы для последующего использования в Delphi. Ну а подключение ActiveXDLL к Delphi проекту это не проблема.

Еще одним существенным ограничением, не позволяющим использовать всю мощь ADO в программе на Delphi, является невозможность приведения к типуOleVariant, таких объектных типов, как ParameterConnection и др., хотя соответствующие описания методов, импортированных из ADODLL и TLB предполагают такое преобразование, например, посмотрим на метод Execute объекта Command:

function Execute(out RecordsAffected: OleVariant; var Parameters: OleVariant; Options: Integer): Recordset; safecall;

Здесь в качестве аргумента Parameters должен передаваться указатель на коллекцию Parameters объекта Command, которая содержит параметры для вызова хранимой процедуры, однако при попытке сделать это, вы получите исключение во время выполнения программы (EOleException), хотя компилятор в данном случае «проглотит» ошибку. К аналогичной ошибке, только более многозначительной (EAccessViolation) приведет и попытка установить свойство объекта Command.ActiveConnection посредством метода Set_ActiveConnection с передачей параметра типа Connection, однако в данном случае есть возможность установитьActiveConnection через переменную типа WideString (желающие могут поэкспериментировать!), что видно из соответствующих описаний:

procedure Set_ActiveConnection(const ppvObject: _Connection); overload;

procedure Set_ActiveConnection(const ppvObject: WideString); overload;

Сказанное выше практически означает, что в Delphi невозможно использовать тесное взаимодействие объектов ADO — RecordsetCommandConnection и Parameter без привлечения адаптированных инкапсулирующих библиотек. Идеальным вариантом, как уже отмечалось выше, является интеграция компонентов, разработанных на VB и Delphi и разделяющих между собой те возможности, которые наиболее полно можно реализовать в каждом из языков.

В начало страницы

Заключение.

В данной статье были рассмотрены некоторые аспекты программирования ADO объектов на VB и Delphi, а также показаны принципиальные подходы в их использовании на примерах. Надеюсь, что данная информация окажется полезной тем разработчикам, которые по каким-либо причинам отказываются от использования BDE, а также тем, кто желает совместно использовать ADO и BDE в своих проектах. Кстати следует заметить, что доступ к данным только через ADO позволяет не включать в дистрибутивы программ компоненты BDE, что значительно позволяет уменьшить объем дистрибутива. Важно и то, что использование ADO ни в коем случае не предполагает полный отказ от BDE, а лишь позволяет найти нетрадиционное решение проблемы, глубже понять интерфейсы OLEDB и принципы низкоуровнего доступа к базам данных. Что касается DataEnvironment, то с его помощью возможно быстрое создание приложений-клиентов для доступа к СУБД, используя в основном только стадию визуального проектирования. Такой подход может быть полезен в случае создания множества простых приложений, не требующих существенных затрат на разработку, но в то же время обладающих большими возможностями по сравнению с приложениями, созданными в рамках MicrosoftAccess.

В начало страницы

Hosted by uCoz