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

"Аудит-Оптим"

ascDB - курсоры в 
многоуровневых приложениях

Что бы вы сказали, услышав об отечественной разработке, классом превосходящей, например, Borland или Microsoft? Несомненно, первой реакцией будет скепсис. Почему-то считается, что отечественная программистская братия способна только на создание "примочек" к именитым пакетам, некрупных игрушек и систем распознавания чего угодно с автоматическим переводом в валюту заказчика. Подразумевается, что все способные на серьезную работу уже спокойно трудятся в стране, ограниченной Тихим океаном с запада, Атлантическим - с востока и притоком иммигрантов - интеллектуально. Но не спешите давать волю своему скепсису! Даже если обещания разработчиков не полностью соответствуют истине, по справедливости стоит дать им хотя бы шанс доказать собственную правоту.

Итак, что же такое ascDB?

ascDB - это набор ActiveX'ов, позволяющих упростить работу с БД в многоуровневой распределенной вычислительной среде. В качестве сервера приложений можно использовать любое Windows-приложение, работающее под управлением Windows NT 4.0 (NT), Windows 9x (с установленным DCOM9x) или Windows 2000 (W2K). В NT и W2K такое приложение может быть сервисом (службой). Самым же удобным вариантом является использование в качестве сервера приложений Microsoft Transaction Server (MTS) в Windows NT и Windows 9х или Component Services (COM+) в Windows 2000. ascDB основан на DCOM и пользуется всеми преимуществами Automation marshaling'а при передаче данных по сети или между процессами ОС.

Каковы преимущества использования ascDB? Начнем с наиболее общих моментов.

Прежде всего, это возможность использования ЛЮБЫХ (для представителей интеллектуального большинства повторяю - ЛЮБЫХ!) средств разработки, доступных на Wintel-платформе. Единственное условие - поддержка COM. Не так и мало, не правда ли? Каждый, пытавшийся создать компоненты, одинаково хорошо работающие и с продуктами Microsoft, и с продуктами Borland (не говоря о Sybase или Oracle), оценит эту деталь. Действительно, библиотека ascDB позволяет создавать приложения на С/С++, VB, VJ++, Delphi, Cobol и других совместимых c Automation языках и средствах разработки. Любой из перечисленных языков можно применять как для создания серверной, так и для клиентской части вашего приложения. Интересный момент - можно использовать и скриптовые языки, например, JavaScript или VBScript, и, разумеется, VBA. Это позволяет вызывать ascDB из броузеров, использовать ее в Интернет-приложениях или просто в документах Microsoft Office или Visio.

Да, кстати. Вы можете превратить вашу любимую "1С Предприятие" в полнофункциональную среду разработки надежно масштабируемых приложений в многоуровневой архитектуре клиент-сервер. Дело в том, что ascDB обеспечивает сетевое взаимодействие, точнее, сетевое взаимодействие обеспечивается средствами DCOM, но через компоненты ascDB. Это дает возможность избавиться от драйверов конкретного производителя на рабочих станциях. Клиентские машины должны иметь только зарегистрированные ascDB-компоненты - несколько DLL-библиотек. Их регистрация не составляет труда, и производится либо вручную, либо с помощью небольшого по размеру инсталлятора, распространяемого через Интернет.

Обычная ISAM-база данных (типа Dbase, Paradox, Access), способна с помощью ascDB стать полнофункциональной клиент-серверной базой данных. ascDB можно за это любить или ненавидеть. Ненавидеть, если вы - продавец большого и дорогого SQL-сервера. Любить - если вы поставщик решений для конечного пользователя, нуждающийся в гладкой масштабируемости от версии для Access или MSDE до варианта, использующего Enterprise Edition сервера Oracle или MS SQLServer.

Двухуровневая архитектура принципиально не позволяет произвольно масштабировать приложения. На пути масштабирования неизбежно встают проблемы совместимости. Например, приложение, созданное для MS SQLServer, практически нельзя перенести под Oracle 8 без потерь в функциональности и производительности. Это же касается и любых других серверов - назовите любую пару по желанию. Для такого переноса приложение нужно переписать целиком - хотя бы, чтобы использовать характерные для Oracle преимущества.

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

В двухуровневой архитектуре любое претендующее на универсальность приложение должно использовать минимальный набор SQL, жестко соответствующий стандарту (причем не SQL'1999, а SQL92). Это приводит к снижению производительности из-за резкого роста количества сетевых вызовов. ascDB упрощает жизнь и в этом случае. Серверный компонент может располагаться на той же машине, что и сервер БД, или подключаться по более быстрому каналу связи. Меньшая латентность канала связи снижает критичность большого количества вызовов - сравните пропускную способность Internet-канала и работу с локальной БД.

Идет немало религиозных споров о преимуществах языков - кто лучше, PL-SQL, Transact-SQL или другие диалекты от разных производителей. Мы предлагаем вместо замечательных, и отчасти даже объектно-ориентированных скриптовых языков использовать примитивные объектно-ориентированные языки (типа С, С++, Object Pascal, Visual Basic, Java и др.). При этом в любом из этих языков вы получите полную функциональность библиотеки ascDB и визуальные средства, с ней поставляемые.

Что означает оптимизация ascDB для многоуровневой среды? Продуманное разделение компонентов на серверные и клиентские и гибкое их взаимодействие обеспечивают наивысшую производительность в многоуровневой сетевой среде. ascDB оберегает от крайностей - постоянных "дерганий" сети или передачи огромных объемов информации за один вызов. Информация разбивается на блоки на серверной стороне и прозрачно для пользователя стыкуется на стороне клиента. Клиент воспринимает информацию как прокручиваемый курсор.

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

ascDB ориентирована на работу с большими объемами данных. Если нужно создавать курсоры на запросах, возвращающих тысячи записей, то ascDB дает создавать forward-only-курсоры, позволяющие моментально получать первый блок данных и докачивать информацию с сервера по мере необходимости. Это кардинально отличается от Microsoft ADO, которое позволяет создать курсор только в keyset-режиме, что при взаимодействии с SQL-серверами приводит к большим потерям времени на стороне сервера. Но ascDB не заставляет использовать именно однонаправленные курсоры, а гибкая технология кэширования скрывает все нюансы взаимодействия с ними.

Немаловажный для наших мест аргумент - стоит недорого.

Для доступа к БД ascDB использует OLE DB. Это позволяет пользоваться любыми СУБД, для которых реализован OLE DB-провайдер или имеется ODBC-драйвер. В основном отладка производилась на MS SQL Server 7.0/7.5 beta 1, но в ближайшем будущем планируется обеспечение поддержки и соответствующей оптимизации для MS Access и основных SQL-серверов. Скорее всего, в число первых войдут: IBM DB2, Oracle, Informix, InterBase. Драйверы для конкретных БД нужны только на сервере. Такое положение вещей очень удобно для ввода в эксплуатацию систем с большим количеством рабочих мест, так как не надо возиться с драйверами на каждом из них. Особенно это актуально, если приложение должно работать с серверами разных производителей. Для отладки приложений удобнее обеспечить на компьютере разработчика как клиентскую, так и серверную части ascDB, включая драйверы БД. Это позволит производить отладку обеих частей приложения, прозрачно переходя из клиентского приложения в серверные объекты и обратно.

И, наконец, главное. Зачем она нужна, эта ascDB?

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

Клиент/Сервер и Клиент vs. Потребитель

Прежде чем продолжить, сделаем небольшое отступление и разберемся с терминологией. Под сервером далее будет пониматься запущенное на сервере приложение, в задачи которого входит публикация данных через ascDB-курсоры. Такое приложение также называется ascDB-провайдером. С сервером все просто, а вот с клиентом может возникнуть путаница. Дело в том, что большинство людей, произнося слово "Клиент", подразумевают нечто, запускаемое на рабочей станции, саму рабочую станцию или человека, работающего на такой станции. В нашем случае это не так. Вернее, не совсем так. С одной стороны, курсоры предназначены для использования в приложениях, запускаемых на клиентских рабочих станциях. С другой, курсоры можно использовать для передачи данных на другие серверы в сети, в другие серверные приложения, запущенные на том же компьютере, и для доступа к БД внутри самого сервера. Поэтому будьте внимательны, далее слово "Клиент" будет в основном расшифровываться как "Потребитель". Физическое положение клиента (Потребителя) не столь важно, ведь DCOM-marshaling стирает границы не только между процессами на одном компьютере, но и делает прозрачными (неотличимыми от локальных) вызовы методов удаленных объектов.

Доступ к данным с помощью курсора

В ascDB нет (ну, или почти нет) средств передачи курсора клиенту. Поэтому вам придется создать COM-объект, через параметры методов которого клиент будет получать курсор. Иначе и не может быть, так как иначе теряется весь смысл использования сервера приложений, а сама архитектура превращается в двухуровневую. Как я уже говорил, в задачу ascDB входит облегчение работы с БД именно в рамках многоуровневой архитектуры клиент-сервер.

Объект может быть оформлен как EXE-сервер или находиться в DLL-библиотеке. Если объект помещен в EXE-модуль, его можно вызывать без дополнительных средств. Для работы с размещенными в DLL-библиотеке объектами необходимо воспользоваться MTS или COM+. В принципе, DLL-объекты можно запускать и напрямую, как инпроцесс-серверы, но это обычно применяется в отладочных целях или для создания однопользовательских версий сетевых многопользовательских продуктов. Чтобы заставить компонент запускаться не как инпроцесс-сервер, а в сервере приложений, надо всего лишь зарегистрировать его соответствующим образом.

Общие принципы

Итак, вы хотите создать объект, метод которого должен возвращать курсор. Что необходимо сделать для этого? 

Да попросту добавить в нужный метод параметр типа IascCachedCursor и пометить его как [in, out] или [out]. На VB это будет выглядеть как ByRef ParamName As ascCachedCursor, на VC 6.0 как IascCachedCursor** ppCashedCursor.

Компоненты в библиотеке ascDB делятся на пять групп.

1-я - серверные компоненты. В их задачи входит выполнение SQL-запросов, как возвращающих курсоры, так и не возвращающих таковых, обработка записи изменений в БД, управление транзакциями и настройка параметров источников данных. Эти компоненты находятся в библиотеке ascRemoteDB.

2-я - невизуальные клиентские компоненты (в дальнейшем локальные компоненты). Они обеспечивают простой навигационный доступ к данным курсора, кэширование данных, блочный прием данных от серверных компонентов, передачу измененных данных серверным компонентам для записи в БД, настройку свойств курсора и его колонок, уведомления о событиях. Эти компоненты находятся в библиотеке ascLocalDB.

3-я - невизуальные элементы управления ActiveX (НЭУ) обеспечивают загрузку удаленных компонентов, вызов у этих компонентов методов, возвращающих курсоры, подключение визуальных элементов управления, а так же большинство возможностей, реализуемых 2-й группой. Практически все перечисленные возможности можно настраивать как в программном режиме (из кода во время выполнения), так и с помощью визуальной настройки во время разработки. Эти компоненты находятся в библиотеке ascLocalDB.

4-я - визуальные элементы управления ActiveX (ВЭУ) обеспечивают визуальное представление и редактирование данных, гибкий механизм расширения функциональности, уведомления о событиях (большинство из которых переопределяют обработку события 2-й и 3-й групп). Эти компоненты в основном находятся в библиотеке ascDbControls.

5-я - сервисные компоненты. Они обеспечивают: облегчение и ускорение выполнения некоторых функций разработки и администрирования. Зачастую сервисные компоненты создаются парами: первый компонент обеспечивает основную функциональность и работает на сервере, а второй является визуальным элементом управления ActiveX и обеспечивает графический интерфейс пользователя (GUI). Эти компоненты находятся в библиотеках ascLocalBaseControls и ascRemoteBaseControls.

Взаимодействие между ними выглядит следующим образом:

  1. Клиент (с помощью CoCreateInstance (С/C++), CreateObject (VB)) загружает серверный компонент. Не забудьте, что загружаемый компонент не входит в состав ascDB. Это ваш компонент! Если курсор будет использоваться для визуальной навигации (в ВЭУ), то проще для загрузки объекта использовать специальный НЭУ - ascLoader. С помощью ascLoader можно во время разработки визуально выбрать необходимый пользовательский компонент на сервере. Если при этом установить свойство ascActive в True, выбранный компонент будет автоматически создаваться в момент создания формы, на которую помещен ascLoader. В противном случае необходимо будет устанавливать значение ascActive во время выполнения из кода. 
  2. Клиент вызывает метод удаленного объекта, получая курсор в переменную типа IascCachedCursor**. Собственно, если курсор используется только программно, то на этом все (см. ascCachedCursor). Если курсор предполагается использовать в ВЭУ, то вместо ascCachedCursor лучше воспользоваться ascVisualCursor. Его вовсе не надо создавать динамически. Вместо этого лучше создать его визуально, поместив во время разработки на форму. ascVisualCursor позволяет вызывать метод удаленного объекта (при этом сам объект должен быть предварительно загружен объектом ascLoader). Как и в случае с объектом ascLoader, ascVisualCursor можно визуально настроить во время разработки и с помощью свойства ascActive указать, надо ли производить вызов метод автоматически во время загрузки формы. В момент настройки ascVisualCursor можно подключить к ascLoader'у, настроить параметры курсора, задать значения по умолчанию для параметров вызываемого метода, настроить колонки курсора. Можно также добавить пользовательские колонки. 
  3. Далее происходит визуальное отображение (не визуальная обработка) и/или редактирование данных. Как и в предыдущих случаях, перечисленные выше действия можно произвести в коде программы или вывести данные в специальные визуальные компоненты. Сейчас существует три ВЭУ, которые можно подключить к ascVisualCursor. Первый из них ascVDBGrid - таблица наподобие DataGrid из поставки VB. Второй - ascDataField - поле. Третий, ascComboBox - выпадающий список. Все компоненты поддерживают так называемые сменные редакторы и сменные рисовальщики. Сменный редактор представляет собой ВЭУ, который открывается в ячейке ascGrid или в рабочем поле компонентов ascDataField. Эти компоненты занимаются взаимодействием с пользователем. Рисовальщики занимаются отрисовкой области ячейки, когда она отображается в ascGrid, ascDataField или ascComboBox и не перекрыта редактором. Рисовальщик - это простой COM-объект, реализующий специальный COM-интерфейс. Вы можете использовать редакторы и рисовальщики, входящие в поставку (причем в большинстве случаев необходимые объекты будут подключаться автоматически на основе информации о типе колонки). Или, если вас не удовлетворяет функциональность входящих в поставку редакторов или рисовальщиков, можно создавать их самостоятельно. Для этого пригодны любые средства разработки, умеющие создавать COM-объекты и ВЭУ ActiveX. Подключить редакторы и рисовальщики к конкретной колонке можно, или непосредственно указав их в свойствах колонок, или присвоив колонке определяемый пользователем тип данных, предварительно назначив необходимый редактор и/или рисовальщик для этого типа в специальном сервисном компоненте. Самое интересное, что практически любые настройки колонок можно произвести как на локальной стороне у компонентов ascCachedCursor, ascVisualCursor и ascGrid, так и на сервере (у того же ascCachedCursor. Исключением не являются ни пользовательский тип данных, ни расширенные свойства колоног, ни редактор, ни даже отрисовщик. В дополнение к стандартным свойствам для каждой колонки можно задать любое количество расширенных (extended, определяемых пользователем) свойств типа Variant. Они тоже могут перемещаться по сети в составе курсора. 
  4. Изменение данных производятся программно, через методы ascCachedCursor (ascVisualascCursor), или интерактивно в ascGrid, ascDataField или ascComboBox. Измененные данные автоматически записываются в буфер и посылаются на сервер для записи в БД. Такая посылка может делаться автоматически при сходе со строки или ячейки, а также при вызове метода Save компонента ascCachedCursor или ascVisualCursor. Указать сценарий записи можно в свойстве rsUpdateType этих компонентов. Вы можете вмешаться в процесс записи на клиенте, подключившись к событиям компонентов ascCachedCursor, ascVisualCursor, или на сервере (см. "Редактируемый курсор"). Можно также подключится к событиям объекта ascServer и обрабатывать все измениия в БД. Эта возможность аналогична триггерам БД, но вместо спицефичных для конкретного СУБД диалекта вы можете использовать любимый (компилирующий, или интепретирующися) язык программирования. Для облегчения процесса записи на сервере можно воспользоваться компонентом ascCursorChanger.

Схема передачи курсора между клиентом и сервером

 

  1. Клиентское приложение выполняет вызов (опосредованно, пользуясь компонентом ascVisualCursor) метода пользовательского серверного компонента. 
  2. Пользовательский серверный объект, пользуясь серверными ascDB-компонентами, выполняет SQL-запрос, создает курсор и возвращает его клиенту через параметр Cursor. 
  3. ascVisualCursor подключает к себе курсор, переходит в активное состояние и оповещает об этом все подключенные к нему визуальные ActiveX'ы. 
  4. Визуальный ActiveX ascGrid получает уведомление об активизации, отображает данные и позволяет производить навигацию и редактирование данных.

Обработка ошибок

В ascDB используется стандартный для COM метод обработки ошибок. При возникновении ошибки ее код, текстовое сообщение и некоторая дополнительная информация присваиваются потоку, в котором она произошла. Это делается с помощью функции API SetErrorInfo. При таком подходе информация об ошибке беспрепятственно передается по сети (если вызывающая программа находится на другом компьютере). VB поступает точно так же. Если вызов делается не из кода, а из визуального компонента, то, получив ошибочный код возврата, он выводит окно сообщения и прекращает выполнение текущей операции. Создав обработчик специального события, можно произвести свою обработку ошибочной ситуации. Если вызов делается из программы, вам необходимо самостоятельно обработать сообщение об ошибке. Например, в VB - вставив оператор On Error ...

Создание пользовательского компонента, возвращающего курсор

В нашем примере в качестве сервера мы используем MS SQLServer 7.х с установленной БД Northwind.

Создадим в VB новый проект. В диалоге создания проекта выбираем ActiveX.DLL.

Рис. 1. Создание нового проекта

Откроем свойства проекта и изменим настройки так, как показано на рис. 2.

Однопоточная модель ("Single Threaded" ) выбрана, чтобы VB не помогал нам и не создавал отдельные копии глобальных переменных для каждого созданного объекта. В MTS/COM+ вместо этого можно использовать "shared properties". Нажмем OK, чтобы закрыть проект.

Рис. 2. Свойства проекта

Теперь переименуем созданный по умолчанию класс с Class1 на Customers и установим свойство MTSTransactionMode этого класса в NoTransactions (см. рис. 3).

Рис. 3. Свойства объекта Customers

 

Теперь добавим ссылки на ascDBRemote.dll,  ascDBLocal.dll, ascDBShared.dll как это показано на рис. 4.

Теперь добавим метод, который должен возвращать курсор:

Public Sub GetCustomersList(ByRef Cursor As ascCachedCursor)
    CurSes.Execute Cursor, "select * from customers"
End Sub

Сохраним проект (File\Save Project) и произведем первую компиляцию (File\Make NorthWind.dll). Теперь снова откроем свойства проекта и на закладке Component выберем опцию Binary Compatibility. Это позволит не перерегистрировать компоненты в MTS или COM+ после каждой компиляции. Нажмите OK и попробуйте еще раз скомпилироваться. Если все нормально, VB предложит вам заменить существующую NorthWind.dll. Ответьте Yes. Файл должен перезаписаться без каких либо дополнительных сообщений. Если ваши ощущения не совпали с описанной мной действительностью, то вы что-то сделали не так ;-).

Естественно, это еще не законченный вариант, но уже сейчас можно попробовать загрузить наш компонент с помощью ascLoader'а.

Если вы хотите сразу приступить к отладке в MTS/COM+, то вам надо зайти на сервер, создать в MTS Explorer/Component Services пакет/приложение и зарегистрировать в нем вашу DLL и ascRemoteBaseControls.dll. В последней находится компонент ascRemoteLoader, он используется ascLoader'ом для получения с сервера списка зарегистрированных компонентов.

Чтобы ascLoader увидел наш компонент, его надо специальным образом зарегистрировать. Для этого откройте ascDB.msc (поставляемый с ascDB файл Microsoft Management Consolе) и выберите в нем ветку "Remote Objects Manager". В появившемся интерфейсе отыщите (в списке справа) наш компонент (NorthWind.Customers), выделите его и нажмите кнопку "<". Название нашего компонента должно переместиться в левый список. Это означает, что он зарегистрирован, и его теперь можно увидеть в списке удаленных компонентов на странице свойств ascLoader'а.

После регистрации можно создать второй проект. Это будет проект простого EXE-приложения с GUI. Переименуем его в VNorthWind и сохраним.

Теперь добавим ссылку на ascLocalDB.dll, как это показано на рис. 4.

Рис. 4. Добавление компонентов из ascLocalDB.dll

Создайте на форме компонент ascLoader (по умолчанию он должен называться ascLoader1). В его свойствах найдите свойство ascObjectName. Выделите его. Появится кнопка выпадающего списка. Нажмите на нее и из появившегося списка выберите название нашего компонента. Установка значения свойства ascActive в True приведет к загрузке нашего компонента. Проделайте это. Если свойство не изменилось, то у вас неправильно установлены ascDB-компоненты, или вы допустили ошибку в одном из предыдущих шагов.

ascLoader умеет загружать ascRemoteLoader с любой машины в сети. Для этого на его странице свойств в поле "Сервер" надо указать имя сервера и нажать Enter (или выбрать сервер из списка, нажав на кнопку "..." рядом с полем).

Настала пора наполнить методы нашего серверного объекта содержанием. Сохраните VNorthWind и откройте наш исходный проект (NorthWind).

Для того, чтобы получить доступ к БД нам необходимо создать объект ascServer. ascServer - это singleton-объект. Если кто не знает, то знайте: singleton-объекты загружаются в память только один раз при первом создании. Все, кто пытаются его создать, получат указатель на единую копию. Этот объект предоставляет доступ к списку источников данных. В основном источник данных описывает подключение к БД, но, в принципе, может описывать подключение и к другим источникам данных, представленным OLE DB-провайдерами. Каждый источник данных может иметь любое количество своих (уникальных) параметров, но для основанных на БД источников обычно используется некоторый поднабор параметров: "Provider" - имя OLE DB-провайдера, "Data Source" - имя сервера, "Initial Catalog" - имя БД, "User ID" - имя пользователя, "Password" - пароль. Каждый провайдер может содержать любое количество дополнительных параметров. Например, практически все SQL-серверы поддерживают параметр "Connect Timeout" - время ожидания ответа от SQL-сервера до признания попытки соединения неудачной. Заметьте, этот параметр поддерживают только SQL-серверы, для ISAM-БД (например, MS Access'а) он не имеет смысла. Задание неподдерживаемых параметров обычно приводит к неработоспособности источника данных. В ascDB источник данных олицетворяется объектом IascDataSource.

Для упрощения работы мы разделили процессы настройки и создания источников данных. Источник данных можно настроить из snap-in MMC ascDB в ветке Data Source Manager (см. рис. 5). Информация о настроенных источниках данных помещается в реестр сервера. Саму настройку можно производить с любого компьютера в сети.

Когда у объекта ascServer запрашивают источник данных, он лезет в реестр, считывает оттуда информацию и создает запрошенный объект IascDataSource. Точнее, считывание данных происходит только при первом запросе источника данных, во второй раз возвращается источник данных, считанный ранее. Если при создании IascDataSource заполнено поле "Начальное количество сессий" (см. ниже), то сразу создаётся указанное в этом параметре количество сессий. Если при очередном запросе сессии созданного количества не хватит, будет создана дополнительная сессия. "Максимальное количество сессий", как следует из названия, ограничивает количество создаваемых сессий. При достижении предельного числа сессий будет выдано сообщение об ошибке. Если это нежелательно, нужно просто оставить это поле незаполненным. Выпадающий список "Уровень изоляции" позволяет задать уровень изоляции транзакции по умолчанию для данного источника данных. Окно "Флаги" позволяет задать флаги, специфичные для данного провайдера. Применяется для большей совместимости с различными БД. Окно "Строки:" позволяет задать символы начала и конца для конкретных видов строк БД (например для кавычек, комментариев, и т.д.)

Рис. 5. Data Source Manager

Список источников данных (объектов IascDataSource) предоставляет объект ascServer. Получить указатель на конкретный IascDataSource можно обратившись к методу ascServer.Item(...) и задав в качестве индекса строку, содержащую название источника данных. Естественно, источник с таким именем должен быть заранее описан, как об этом говорилось выше. Метод "Item" является методом по умолчанию, поэтому VB программисты могут воспользоваться более простым синтаксисом: ascServer("DataSourceName"). Разделение же "обязанностей" позволяет упростить и ускорить работу с источниками данных. Обеспечить защиту от злоумышленников можно, задав атрибуты защиты веткам реестра, хранящим информацию об источниках данных.

Как вы понимаете, источник данных не самоцель, он нужен нам, чтобы создать сессию (подключение) к описанной источником данных БД. Сессия представлена в ascDB объектом IascSession, он олицетворяет подключение, позволяет управлять транзакциями, выполнять SQL-запросы и т.п. Собственно от IascDataSource нам только и надо, что получить ссылку на IascSession. Для этого следует вызвать метод IascDataSource.GetSession. Единственным параметром данного метода является константа, описывающая требуемый уровень изоляции транзакции. Если параметр не задан, то используется уровень, заданный в  Data Source Manager. Поскольку сам IascDataSource нам в общем-то не нужен, на VB можно просто выполнить нечто вроде: rv.Item("NorthWind").GetSession(ailReadommitted). В данном случае NorthWind - это имя источника данных, а ailReadCommitted - уровень изоляции транзакции для запрашиваемой сессии. Еще более простой способ получить ссылку на  сессию - использовать сессию по умолчанию, вызвав метод IascDataSourceю.GetDefaultSession. При такой форме получения ссылки не требуется указывать уровень изоляции транзакции, так как  всегда используется ailReadCommitted  уровень. Сессия по умолчанию - это специальная системная сессия. Она всегда существует, и используется для системных нужд.  При помощи данной сессии можно только производить чтение(т.е. она ReadOnly).

Получаемая сессия нужна нам только для создания курсоров путем выполнения SQL-запросов типа "SELECT". Курсор может жить сколь угодно долго, и, в сущности, после передачи его клиенту мы никак не сможем повлиять на его срок жизни. Посему мы должны обеспечить отдельную транзакцию для каждого отдельного курсора - так как она определяется на сессию. Другой путь - создавать все курсоры в рамках одной сессии. 

Но это создает некоторые проблемы:
1) есть серверы, которые при некоторых уровнях изоляции в момент фиксации (commit) транзакции разрушают курсоры 
2) слабый уровень изоляции (Read Uncommitted) приводит к появлению фантомов (грязи) 
3) сильные уровни изоляции ailRepeatableRead и ailSerializable приведут к тому, что данные, внесенные из других сессий (другими транзакциями), не будут видны даже вновь создаваемым курсорам, ведь делать commit в нашей сессии некому
4) в этой сессии нельзя модифицировать БД, так как случайно сбойнувший оператор SELECT вызовет откат всей транзакции
5) естественно, в этой сессии нельзя выполнять несколько параллельных циклов модификации данных, это приведет к непредсказуемым последствиям.

Можно, конечно, попробовать создать отдельную сессию для каждого физического человека и фильтровать вызовы по имени пользователя, но кто гарантирует, что под одним именем пользователя не зашли с разных машин, или что пользователь не загрузил вторую копию приложения? В общем, такой подход очень сложен в реализации. А вот для того, чтобы удовлетворить вышеперечисленным требованиям, надо всего лишь установить в сессии уровень изоляции Read Committed и не выполнять в ней SQL-запросов, модифицирующих данные. Если учесть тот факт, что многоуровневая архитектура не позволяет воспользоваться средствами OLE DB для записи изменений в курсоре, то такое решение становится весьма привлекательным (Примечание, не пугайтесь, в ascDB реализована автоматизация записи изменений производимых в курсоре , но о ней речь пойдет немного позже.).

Итак, чтобы сессию можно было повторно использовать, создадим глобальный модуль, и процедуру "Main".  Сделаем процедуру "Main" стартовым объектом, запускающимся при загрузке DLL. Для этого требуется указать в свойствах проекта, что стартовым объектом является "Sub Main" (см. рис. 2). Назовем модуль оригинальным именем "GlobalModule". Вот код, который необходимо поместить в этот модуль:

Global srv As ascServer
Global CurSes As IascSession
Public Sub Main()
   Set srv = New ascServer
   Set CurSes = srv("NorthWind").GetDefaultSession
End Sub

Как видно из этого кода, ascServer мы назвали "srv", а IascSession - "CurSes". Теперь самое время заставить наш объект создавать курсор и возвращать его вызывающему приложению. Для этого переместимся в модуль класса Customers (Customers.cls) и поменяем его код так, чтобы он выглядел следующим образом:

Public Sub GetCustomersList(ByRef Cursor As ascCachedCursor)
  CurSes.Execute Cursor, "select * from customers"
End Sub

Наверное, вы догадались, что метод "Execute" выполняет SQL-запрос и, если все в порядке, возвращает курсор. Этот метод имеет много параметров, но все (кроме двух первых) можно не заполнять. Позже мы рассмотрим каждый из этих параметров, а сейчас намного интересней быстренько откомпилировать наш объект и протестировать его из нашего тестового приложения.

Итак, компилируйте и открывайте VNorthWind.

Откройте форму и бросьте на нее компонент ascVisualCursor. В его свойствах, на странице "Основная" в списке "Список визуальных загрузчиков", выберите ascLoader1 и включите переключатель "Активизировать". Все органы управления, кроме переключателя, должны стать недоступными. Это означает, что вызов метода нашего объекта прошел успешно. Можете деактивировать переключатель и рассмотреть остальные параметры этой страницы свойств. Они описывают вызываемый метод. Объяснять здесь нужно только выпадающий список "Используемый параметр", в нем перечисляются параметры вызываемого метода, которые потенциально могут быть курсором ([in, out] IascCachedCursor**). Если таких параметров несколько, то в этом списке надо указать необходимый (ascVisualCursor может использовать только один курсор). Да, перед закрытием свойств не забудьте перевести переключатель в активное состояние, иначе следующий шаг у вас не получится.

Теперь необходимо подключить визуальные компоненты, и первое приложение можно считать законченным. Визуальные компоненты находятся в библиотеке ascDbControls.dll. Полное название библиотек в VB - "ascDbControls 1.0 Type Library". Подключите ее к проекту. Для этого нажмите правую кнопку мыши на Toolbox'е VB и из появившегося меню выберите пункт Components... Среди появившихся компонентов выберите ascGrid, поместите его на форму, задайте ему подходящий размер и найдите в его свойствах "ascSourceName". Выделите его, и выберите из списка значение "ascVisualCursor1". Как только вы это сделаете, в таблице (grid'е) должны будут отобразиться данные из БД (см. рис. 6). Если этого не случилось, убедитесь, что свойства ascActive компонентов ascLoader и ascVisualCursor установлены в True.

Кстати, вы заметили, что данные стали доступны в режиме разработки? Из меню, выпадающего при нажатии правой кнопки мыши на таблице, вы можете выбрать пункт "Настройка". Это позволит вам побродить по данным и настроить ширины колонок. Если бы курсор был редактируемым, можно было бы даже внести изменения в БД.

Конечно, это очень простой пример. В нем используется очень простой запрос, нет параметров, курсор не поддерживает запись, и не изменяются никакие свойства. Однако он наглядно демонстрирует принципы работы с библиотекой ascDB. Главным преимуществом применения многоуровневой архитектуры является возможность гибкого задания бизнес-правил при обработке и обеспечить дополнительный контроль над данными. В данном случае мы могли бы ограничить доступ к возвращаемым данным, определив атрибуты защиты на весь объект (в MTS/COM+ Explorer), или на уровне отдельного метода программным путем. Например, мы могли бы определить в MTS/COM+ Еxplorer, исполняет ли вызывающий метод человек некую роль, а в коде компонента в зависимости от исполняемой роли возвращать ограниченный или полный список заказчиков (Customer). О том, как это сделать, можно прочитать в документации по программированию для MTS/COM+.

Рис. 6. Так должна выглядеть форма нашего первого приложения

Далее, чтобы продемонстрировать способы работы с ascDB-компонентами, мы дополним наш компонент методом, один из параметров которого будет задавать маску поиска, сделаем курсор редактируемым и обсудим возможности клиентских компонентов по форматированию и расширенному представлению данных. Но прежде давайте внимательнее рассмотрим использованные нами компоненты.

Главной задачей объекта ascServer является предоставление списка источников данных и выполнение некоторых сервисных функций. Например, метод ascServer.SetCursorUserData(Cursor As Variant, UserData As Variant) позволяет подключить к уже созданному курсору ваши данные. В качестве данных можно задать любые поддерживаемый типом "вариант" данные, в том числе и массив. Более подробно рассматривать ascServer сейчас бессмысленно, поэтому перейдем к IascSession.

IascSession олицетворяет собой соединение с БД. Только через сессию можно управлять транзакциями. Все выполняемые запросы, как создающие курсоры, так и нет, создаются в рамках какой-нибудь сессии. Напомним, что имеется специальная системная сессия ("Default"). Она всегда существует, и к ней можно обращаться через методы объекта ascServer. Эта сессия используется для системных нужд. Вы можете использовать её для внутренних нужд, но лучше запросить свою собственную сессию. Как запросить системную сессию было показано в нашем примере. Вообще, сессии надо использовать разумно, с одной стороны, не допуская создания чрезмерно большого их количества, а с другой, грамотно манипулируя уже созданными. Нельзя допускать смешивания действий, принадлежащих разным транзакциям. Как уже говорилось, для создания курсоров можно использовать правильным образом настроенные, "долгоживущие" сессии, а для модификации БД - динамически запрашиваемые экземпляры, освобождая их после завершения транзакции. Чтобы динамический запрос сессий не замедлял работу сервера, можно организовать пул сессий. Параметры пула настраиваются в Data Source-дизайнере MMC консоли.

Рассмотрим методы объекта IascSession.

Первый метод этого объекта предназначен для создания команд. Команды (объекты IascCommand) позволяют создавать курсоры на базе параметризованных запросов. О командах речь пойдет позже. Описание этого метода выглядит следующим образом:

HRESULT CreateCommand(
   [in, defaultvalue(NULL)] IDispatch *Changer, 
   [in, defaultvalue(0)] long CursorID, 
   [out, retval] IascCommand **ppascCommand
); 

или

Function CreateCommand([Changer As Object], [CursorID As Long]) As IascCommand

Первый параметр этого метода - Changer - позволяет задать объект, который будет производить запись произведенных в курсоре изменений (см. ниже). Второй параметр - CursorID - позволяет задать уникальный идентификатор, который позволит вам в процессе записи изменений отделять один курсор от другого.

Следующий метод, Execute, уже знаком нам. Как вы уже поняли, он выполняет SQL-запрос и возвращает курсор. Выполняемый посредством этого метода SQL-запрос не может содержать внешних параметров (только константы). Если необходимо выполнить параметризованный запрос, необходимо воспользоваться объектом IascCommand. Описание этого метода выглядит следующим образом:

HRESULT Execute(
   [in, out] IascCachedCursor **Cursor, 
   [in] BSTR SQL, 
   [in, defaultvalue(NULL)] IDispatch *Changer, 
   [in, defaultvalue(0)] long CursorID, 
   [in, defaultvalue(ascCUR_DEFAULT)] ASC_DB_CURSOR_TYPE DBCursorType, 
   [in, defaultvalue(0)] long FetchBufferSize, 
   [in, optional] VARIANT UserData 
); 

или

Sub Execute(
   Cursor as IascCachedCursor,
   SQL As String,
   [Changer As Object],
   [CursorID As Long], 
   [DBCursorType As ASC_DB_CURSOR_TYPE = ascCUR_DEFAULT],
   [FetchBufferSize As Long],
   [UserData]
)

Параметр Cursor - это, конечно же, курсор. SQL - это строка, содержащая SQL-выражение. 3-ий и 4-ый параметры соответствуют соответственно первому и второму параметрам предыдущего метода. DBCursorType - определяет тип курсора (см. табл. № 1). FetchBufferSize - определяет размер блока записей передаваемого за один вызов по сети. UserData - данные которые можно ассоциировать с создаваемым курсором (они не будут передаваться по сети, зато с их помощью можно хранить в каждом курсоре необходимую информацию). Отдельно надо поговорить о типе курсора (параметр DBCursorType). Как следует из описания метода, этот параметр имеет значение по умолчанию - ascCUR_DEFAULT. ascCUR_DEFAULT - означает, что при создании курсора определение реального типа будет взято из информации, пришедшей в параметре Cursor. Эта информация помещается туда, на клиентской стороне, с помощью объекта ascVisualCursor или ascCachedCursor. ascVisualCursor делает это автоматически, а собственно тип курсора может быть настроен визуально через его свойства. О том, как осуществить настройку типа курсора с помощью ascCachedCursor, будет расказано далее, а пока, так как мы жестко не задали DBCursorType вы можете поэкспериментировать с настройками ascVisualCursor. Все сказанное о DBCursorType справедливо и для FetchBufferSize, но в качестве значения, используемого для указания, что информацию надо брать от клиента, используется "0". Если с клиента не пришла информация о типе курсора и размере буфера, то будут установлены значения: DBCursorType = ascCUR_FORWARD и FetchBufferSize = 100. Если параметр DBCursorType или FetchBufferSize имеет значение, отличное от принимаемого по умолчанию, клиентские настройки игнорируются.

Следующий метод объекта IascSession называется ExecuteImmediate. Он предназначен для выполнения SQL-запросов, не создающих курсоров. Например, запросов, выполняющих изменение данных в БД (Insert, Update, Delete), или вызовы хранимых процедур, так же не возвращающих курсоры и не имеющих выходных (out) параметров:

HRESULT ExecuteImmediate(
   [in] BSTR SQL,
   [out, retval] long *RowsAffected
); 

или

Function ExecuteImmediate(SQL As String) As Long

Таблица № 1

 Описание для перечисления ASC_DB_CURSOR_TYPE

Значение

Описание

ascCUR_DEFAULT

Означает, что значение определяется вызываемой стороной. Если ни вызывающая, ни серверная стороны не определили конкретного значения, то тип курсора будет установлен в ascCUR_FORWARD.

ascCUR_INSENSITIVE

После выполнения запроса курсор не видит изменений данных сделанных другими транзакциями.

ascCUR_KEYSET

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

ascCUR_DYNAMIC

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

ascCUR_FORWARD

Курсор этого типа поддерживает прокрутку только вперед. Но в пределах одного буфера допускаются и другие методы перемещения, а с включенным кэшем можно произвольно перемещаться на любую кэшированную запись. Для большинства SQL-серверов ascCUR_FORWARD — это самый быстрый и нетребовательный к ресурсам режим.

Примечание
Независимо от типа курсора в ascDB используется кэширование данных курсора на клиентской стороне. Поэтому курсоры типов ascCUR_KEYSET и ascCUR_DYNAMIC считывают изменения только при первом считывании (fetch'е). Причем, так как физически считывание производится блочно, нельзя быть уверенным, что считываются обновленные данные с сервера, а не хранящиеся в кэше. Зато кэширование позволяет реализовать высокопроизводительный скролируемый курсор с любым принципом доступа.

SQL -это строка, содержащая SQL-выражение. RowsAffected - это количество обработанных строк, возвращаемое SQL-сервером. Например, количество удаленных строк для оператора Delete.

Далее идут методы, обеспечивающие поддержку транзакций. Первый из них TranBegin. Как следует из его названия, он создает (открывает) новую транзакцию. Вот его описание:

HRESULT TranBegin([out,retval] long *TransactionLevel); 

или

Function TranBegin() As Long

Этот метод не имеет входных параметров. Он возвращает фактический уровень изоляции (TransactionLevel) новой транзакции.

Методы TranCommit и TranRollback соответственно подтверждают или откатывают транзакцию. Эти методы не возвращают никаких значений, но могут возбуждать исключения (C/C++ программисты не должны забывать, что изложение в этой статье ведется в терминах VB, а "возбуждать исключения" означает, что метод вернет ошибку (отрицательный код возврата), при этом дополнительную информацию можно получить через функцию GetErrorInfo.). Вот их описание:

HRESULT TranCommit();
HRESULT TranRollback();

или

Sub TranCommit()
Sub TranRollback()

Последним методом объекта IascSession является TranJoin. Он позволяет подключиться к распределенной транзакции, организованной с помощью Microsoft Distributed Transaction Coordinator (MSDTC). Его описание выглядит следующим образом:

HRESULT TranJoin([in] IUnknown * TransactionCoord); 

или

Sub TranJoin(TransactionCoord As Unknown) 

Выполнение параметризованных запросов

Методы объекта IascSession позволяют выполнять SQL-запросы, но не позволяют задавать параметры для этих запросов. Конечно, простые входные параметры можно задавать, конкатенируя их значения к SQL-выражениям. Когда же речь заходит о возвращаемых параметрах хранимых процедур, или просто о параметрах сложных типов данных, то обойтись только методами IascSession'а уже не удастся. Чтобы вызывать параметризованные запросы, необходимо воспользоваться объектом IascCommand. Это объект, как и IascSesion, нельзя создать с помощью оператора new (CoCreateInstance в C/C++). Его можно получить, вызвав метод CreateCommand у IascSession.

Как уже говорилось ранее, все параметры этого метода являются необязательными. Они необходимы при создании редактируемых запросов. Сейчас мы не будем обсуждать эти параметры, а перейдем к процедуре описания объекта IascCommand.

Итак, перед использованием параметров в объекте IascCommand их надо описать. Для этого надо воспользоваться свойством "Params". Его описание выглядит следующим образом:

[propget, id(DISPID_COMMAND_PARAMS)] 
   HRESULT Params([out, retval] IascParams **pVal);
[propput, id(DISPID_COMMAND_PARAMS)] 
   HRESULT Params([in] IascParams * newVal); 

или

Property Params As ascParams

Params представляет собой список (collection) параметров команды.

Чтобы добавить параметр, необходимо вызвать метод IascCommand.Params.Add ... На самом деле метод Add принадлежит к объекту ascParams, но в VB можно опустить эту подробность. Описание метода Add выглядит следующим образом:

HRESULT Add(
   [in,defaultvalue("")] BSTR Name, 
   [in,defaultvalue(apfIn)] ASC_PARAM_FLAG Flag, 
   [in,defaultvalue(ASC_CDT_STRING)] ascColumnDataType Type, 
   [in,defaultvalue(256)] long Size, 
   [in,optional] VARIANT Value, 
   [out,retval] IascParameter **ppParameter
); 

или

Function Add(
   [Name As String], 
   [Flag As ASC_PARAM_FLAG = apfIn],
   [Type As ascColumnDataType = ASC_CDT_STRING],
   [Size As Long = 256],
   [Value]
) As IascParameter

Name - это имя добавляемого параметра. Если имя не задано, параметр получит имя "ParamX", где X - порядковый номер параметра. Flag - признак, описывающий, является параметр входящим (apfIn), обратновозвращаемым (apfOut) или и тем и другим (apfInOut). Value - позволяет задать значение параметра по умолчанию. Size - максимальный размер для параметров с типами, имеющими переменную длину. Как ни странно, для входящих строковых параметров (типа apfIn) длину указывать не обязательно. Так что в реальной работе придется указывать размер для очень ограниченного количества типов данных.

Если вызов этого метода прошел успешно, он возвращает объект IascParameter. С помощью IascParameter можно дополнительно настроить добавленный параметр, но обычно в этом нет необходимости, так как все параметры можно задать при вызове метода Add.

Кроме стандартных методов управления списком (Item, Count, Add, Remove, Clear и _NewEnum) Params содержит метод Move, позволяющий поменять порядковый номер параметра (переместить его) и свойство CanModify, позволяющее узнать, можно ли в данный момент модифицировать структуру параметров. Если CanModify установлено в False, то можно менять только значения параметров, но не их количество и свойства.

Params является свойством по умолчанию, поэтому в VB можно обращаться к параметрам без явного указания имени этого свойства. Например, можно написать:

cmd.Params("CompanyName") = "F%"

или

cmd("CompanyName") = "F%"

или даже

cmd!CompanyName = "F%"

Для того, чтобы использовать параметр, мало его описать. Его надо еще и обозначить в SQL-запросе. Это делается проставлением знака "?" там, где должен быть подставлен параметр.

Добавим в наш серверный компонент новый метод, возвращающий тот же список, но отфильтрованный по полю "CompanyName".

Вот необходимый для этого код:

Dim cmd As IascCommand
Public Sub GetCustomersListByCompanyNameMask(ByRef Cursor As ascCachedCursor, ByVal CompanyNameMask As String)
    ' Создаем команду только один раз...
    If cmd Is Nothing Then
        Set cmd = CurSes.CreateCommand()
        ' Добавляем описание параметра
        cmd.Params.Add "CompanyName"
	cmd.SQL = "select * from customers where CompanyName Like ? order by CompanyName"
    End If
    cmd("CompanyName") = CompanyNameMask
    cmd.Execute Cursor
End Sub 

Перегенерируем NorthWind.dll и откроем проект VNorthWind. Теперь нам нужно заменить вызываемый метод в ascVisualCursor1. Для этого откроем страницу свойств "Основная" и деактивизируем ascVisualCursor1 переключателем "Активизировать". Теперь из выпадающего списка "Список функций" выберем нашу новую функцию. В списке параметров выберем "CompanyNameMask" и поле "значение параметра" и введем маску. Например, "%" (выводить все записи). Нажмем "Apply" и включим переключатель "Активизировать". Закроем диалог страниц свойств. Добавьте на форму TextBox. В него мы будем вводить маску. Создайте у этого TextBox'а обработчик события Change. Добавьте в обработчик код так, чтобы обработчик выглядел следующим образом:

Private Sub Text1_Change()
    ascVisualCursor1.rsFuncParameter(1) = Text1
    ascVisualCursor1.Refresh
End Sub 

Свойство rsFuncParameter позволяет задать или считать значение параметра вызываемой функции по его порядковому номеру. Параметры должны быть предварительно описаны (мы сделали это в странице свойств.).

Нажмите F5 и протестируйте получившееся приложение. Если вы все сделали правильно, при вводе маски в таблице должны показываться удовлетворяющие этой маске записи. Чтобы протестировать многопользовательский режим работы, вы можете скомпилировать EXE-файл и запустить его несколько раз с одной или нескольких машин.

Только что мы создали пример, работающий с параметризованными запросами. Как говорилось выше, такую функциональность нам предоставляет объект IascCommand.

Вкратце разберем его свойства и методы.

Свойства Changer, CursorID, SQL и методы Execute, ExecuteImmediate аналогичны одноименным методам из объекта IascSession, поэтому мы не будем уделять им сейчас особое внимание. Единственное, что надо сказать - свойства Changer, CursorID, SQL дублируют аналогичные параметры некоторых методов, позволяя задать соответствующие значения единожды вместо того, чтобы задавать их каждый раз (как в IascSession).

Метод Prepare позволяет подготовить запрос. При этом происходит разбор (parsing) SQL-выражения, проверка информации о параметрах, подготовка запроса и т.п. После этого запрос выполняется быстрей и затрачивает меньше системных ресурсов. Однако некоторые серверы производят оптимизацию и без использования Prepare, и может так сложиться, что общая производительность от использования этого метода будет увеличиваться несущественно или даже снижаться. Например, Prepare с MS SQLServer 7.0 дает незначительное ухудшение производительности по сравнению с использованием не подготовленных, но параметризованных SQL-запросов. К тому же при использовании Prepare (с MS SQLServer 7.0) начинаются некоторые проблемы с параллельным использованием команды. Не стоит также забывать, что применение Prepare с другими серверами может дать существенный выигрыш в производительности. Но все же будьте осторожны с применением Prepare. Вот его описание:

HRESULT Prepare(
   [in, defaultvalue("")] BSTR SQL,
   [in, defaultvalue(FALSE)] VARIANT_BOOL GetParams
); 

или

Sub Prepare([SQL As String], [GetParams As Boolean = False]) 

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

После подготовки запроса нельзя изменять никакие свойства команды за исключением значений параметров. Для изменения других свойств команду надо перевести в неподготовленное состояние методом Unprepare. Он не имеет параметров и может только возбуждать исключительную ситуацию в случае, если команда не была подготовлена. Определить, находится ли команда в подготовленном состоянии, можно по свойству IsPrepared. Оно имеет тип Boolean.

Таблица № 2

Описание для перечисления ASC_SUPPORT_MULTIPLE_RESULTS

Значение

Описание

smrNotSupported

Возврат нескольких курсоров не поддерживается.

smrSupported

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

smrConcurrent

Возврат нескольких курсоров поддерживается. Курсоры могут использоваться одновременно (На конец второго тысячелетия ни один SQL-сервер не поддерживал этой функциональности.).

 Некоторые, но далеко не все, серверы поддерживают возврат нескольких курсоров за один вызов метода Execute. Определить, поддерживает ли сервер возврат множественных курсоров, можно по значению свойства SupportMultipleResults. Оно имеет тип ASC_SUPPORT_MULTIPLE_RESULTS (см. табл. № 2) и доступно только для чтения.

Чтобы получать несколько курсоров, предварительно надо перевести команду в соответствующий режим. Сделать это можно с помощью свойства MultipleResults, оно имеет тип Boolean и может быть как установлено, так и считано.

Первый курсор становится доступным сразу после выполнения метода Execute. Чтобы получить следующий курсор, необходимо вызвать метод GetNextResult. Помните, что если провайдер поддерживает только тип smrSupported, то перед вызовом GetNextResult вам необходимо закрыть предыдущий курсор. Такое ограничение приводит к тому, что клиенту можно возвратить только последний курсор в списке. Остальные курсоры можно безопасно использовать только на сервере.

Вот описание метода GetNextResult:

 HRESULT GetNextResult(
   [in,out] ASC_CURSOR *Cursor,
   [out,retval] ASC_GET_NEXT_RESULT_CODE *pCode
); 

или

 Function GetNextResult(Cursor As Variant) As ASC_GET_NEXT_RESULT_CODE 

Возвращает следующие значения: gnrcNoResult - больше нет результатов, gnrcNoCursor - результат есть, но курсор не создался и gnrcCursor - получен курсор.

Редактируемый курсор

Итак, мы создали курсор на базе параметризованного запроса, но наш курсор нельзя редактировать! Следующим шагом мы сделаем наш курсор редактируемым.

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

Концепция записи изменений

Концепция ascDB предполагает, что между сервером приложений (где загружается библиотека ascRemoteDB) и сервером БД (или подсистемой хранения информации, далее сервер данных - СД), взаимодействие происходит по более быстрым каналам связи, нежели с клиентом (где загружается библиотека ascLocalDB). ascCursorChanger отвечает за преобразование присланных с клиента изменений в последовательность действий, модифицирующих данные. Таким образом, достигается максимальная производительность, уменьшается сетевой трафик, и расширяются возможности модификации данных за счет использования уникальных возможностей объекта записи - ascCursorChanger.

При записи измененные данные посылаются серверу приложений компактно упакованными за один сетевой вызов, а вызовы к серверу данных могут быть множественными и сколь угодно сложными, одним словом, рассчитанными на более быстрое и надежное соединение. Хорошим примером является MS Access. Работа с ним на локальной машине суперпроизводительна и довольно стабильна. Отдельные вызовы по модификации данных выполняются молниеносно, чего нельзя сказать о работе с ним в сетевом режиме. Если воспользоваться для доступа к БД Access компонентами из библиотеки ascDB, то общий результат будет практически схож по надежности и производительности с результатами, которые можно получить при использовании именитого SQL-сервера.

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

Технология обмена информацией, предусмотренная библиотекой ascDB, предполагает передачу по сети минимального количества информации. Для записи изменений в БД передаются только сами изменения, без излишеств.

В концепцию сохранения изменений заложена идея автоматической генерации SQL-запросов объектом ascCursorChanger. При этом генерируются запросы добавляющие (INSERT), изменяющие (UPDATE) и удаляющие (DELETE) данные в таблицах. На практике используются и другие методы изменения данных (например, навигационный способ для ISAM-БД). Настройка ascCursorChanger не зависит от конкретного механизма модификации данных.

Изменение данных основывается на:

После записи изменений на клиентскую сторону отправляется массив, содержащий данные, измененные или появившиеся непосредственно при модификации таблиц БД. Например, при добавлении записей в таблицу, содержащую колонку с автоприращением (IDENTITY-колонку в MS SQL Server). Для отправки данных клиенту предварительно необходимо пометить специальным признаком соответствующие колонки.

Массив изменений

Массив изменений, произведенных клиентом, формируется и посылается на сторону сервера при вызове метода ascCached[Visual]Cursor.Save(). Структура этого массива для нас малоинтересна, поэтому будем считать, что это просто BLOB (двоичная информация). Пересылка на сервер происходит автоматически. При этом происходит вызов метода Save скрытого объекта ascCursor, размещающегося на сервере, указатель на который входит в состав курсора (см. рис. 7). В этом массиве помимо самих изменений находятся тип операции (INSERT, UPDATE или DELETE) и значение так называемых ключевых полей (о них речь пойдет немного ниже).

Рис. 7. Путь информации при записи изменений

Структура таблиц и колонок БД

Для того, чтобы ascCursorChanger мог производить запись, разработчик должен в необходимом объеме описать таблицы подлежащие модификации. Многие серверы БД позволяют различными способами изучить структуру базы данных (таблиц, свойств колонок), но при каждом создании нового объекта ascCursorChanger получение такой информации могло бы занимать неоправданно большое количество времени. Поэтому в библиотеке ascDB предлагается другой способ. Разработчик может собственноручно описать структуру таблиц БД, либо вручную (внутри методов пользовательского объекта, находящегося на стороне сервера), либо с помощью визуального интерфейса, позволяющего интерактивно настроить параметры объекта ascCursorChanger, сохранить настройки под определенным именем в специальном хранилище и, впоследствии, загружать такие поименованные объекты (см. "Визуальный дизайнер команд").

Настройка объекта ascCursorChanger

ascCursorChanger поддерживает запись в несколько таблиц. Для этого необходимо настроить свойство (Tables). Это свойство и свойства входящих в него объектов определяют соответствие колонок курсора и колонок физических таблиц БД.

Рис. 8. Схема записи изменений.

Свойство Tables является списком (collection), каждый элемент которого содержит описание одной таблицы. Описание каждой таблицы содержит подразделы, отвечающие за добавление, удаление и изменение данных. Каждый из этих подразделов представлен списком, содержащим описание колонок таблицы. Каждое описание, в свою очередь, представляет описание колонки (название, тип данных, максимальный размер, и т.п.) в БД и дополнительной информации (номер или имя колонки курсора, значение из которой должно быть записано в это поле, признак, говорящий о том, что значение этой колонки после выполнения записи должно быть возвращено клиенту). Доступ к такому описанию поля производится следующим образом:

 Tables("ИмяТаблицы").Insert("Имя или индекс колонки").XXX 

Например, следующая строка позволяет установить "колонку-источник" для колонки "CompanyName" таблицы "customers" БД "Northwind":

 cmd.Tables("customers").Insert("CompanyName").SourceColumn = "CompanyName" 

Это строка говорит объекту ascCursorChanger, что при вставке данных в таблицу "customers" данные для поля "CompanyName" надо брать из одноименной колонки курсора. Вместо имени колонки можно задавать порядковый номер колонки курсора.

На рис. 9 представлена структурная схема описания таблиц объекта ascCursorChanger.

Как видно из рисунка, описание колонок различно для разных операций. Это вызвано тем, что для разных операций требуется разная информация. Так, для операции удаления (Delete) нужно всего лишь указать ключевые поля (поля, по значению которых будет однозначно определяться удаляемая строка). Для операции добавления (Insert) необходимо указать соответствие колонок таблицы и курсора. Кроме этого, нужно также указать ключевые поля, пометив их, в случае необходимости, как обратновозвращаемые - piotOutAlways, как поле с автоприращением - avspFromIdentity, как поле, для которого ascCursorChanger должен сгенерировать новое уникальное значение - avspFromNewID и т.п. Для изменения (Update), как и в предыдущем случае надо указать ключевые поля и указать соответствие колонок таблицы колонкам курсора.

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

Запись изменений

Создадим объект класса ascCursorChanger с именем Changer. Один объект ascCursorChanger можно использовать для любого количества курсоров. Главное, чтобы курсор, чья информация записывается, соответствовал описанию, хранящемуся в ascCursorChanger'е. Исходя из этого, объявим Changer как глобальную переменную.

Global Changer As New ascCursorChanger

Его настройку удобно сделать в процедуре "Main". С учетом изменений GlobalModule должен выглядеть следующим образом:

Global srv As ascServer
Global CurSes As IascSession
Global Changer As New ascCursorChanger
Public Sub Main()
    Set srv = New ascServer
    Set CurSes = srv("NorthWind").GetSession(ailReadCommitted)
    'Добавление таблицы, в которой будут производиться изменения.
    Changer.Tables.Add "customers"
    'Настройка добавленой таблицы
    With Changer.Tables("customers")
        'Настройка операции добавления (Insert)
        .InsertSQLParamAdd "CustomerID"
        .InsertSQLParamAdd "CompanyName"
        .InsertSQLParamAdd "ContactName"
        .InsertSQLParamAdd "ContactTitle"
        .InsertSQLParamAdd "Address"
        .InsertSQLParamAdd "City"
        .InsertSQLParamAdd "Region"
        .InsertSQLParamAdd "PostalCode"
        .InsertSQLParamAdd "Country"
        .InsertSQLParamAdd "Phone"
        .InsertSQLParamAdd "Fax"
        
        'Настройка операции изменения (Update)
        .UpdateSQLParamAdd "CustomerID", Flag:=acufKey
        .UpdateSQLParamAdd "CompanyName"
        .UpdateSQLParamAdd "ContactName"
        .UpdateSQLParamAdd "ContactTitle"
        .UpdateSQLParamAdd "Address"
        .UpdateSQLParamAdd "City"
        .UpdateSQLParamAdd "Region"
        .UpdateSQLParamAdd "PostalCode"
        .UpdateSQLParamAdd "Country"
        .UpdateSQLParamAdd "Phone"
        .UpdateSQLParamAdd "Fax"
        
        'Настройка операции удаления (Delete)
        .DeleteSQLParamAdd "CustomerID"
    End With
End Sub 

Рис. 9. Структура описания таблиц в объекте ascCursorChanger

Таблица № 3

Описание для перечисления ASC_CHANGER_INSERT_VALUE_SOURCE

Значение

Описание

acivsFromChanges (используется по умолчанию)

Брать значение из массива изменений, пришедшего от клиента.

acivsFromDefault

Брать значение из параметра DefaultValue.

acivsFromIdentity

Считывать значение автоинкрементального поля.

acivsFromNewID

Генерировать новое уникальное целочисленное значение.

acivsFromString

Брать значение из параметра DefaultValue, но подставлять это значение как строку. Таким образом, можно указать, например, выражение или функцию.

acivsFromUserData

Брать значение из атрибута UserData, хранящегося в курсоре на серверной стороне. Атрибут UserData можно задать при вызове метода Execute объектов IascCommand, IascSession и ascServer, а также назначить непосредственно созданному курсору с помощью метода SetCursorUserData объекта ascServer.

Методы xxxSQLParamAdd позволяют за один прием задать все атрибуты одной колонки. У этих методов много параметров, но почти все они являются необязательными. Главное при вызове этих методов - указать соответствие между колонкой из БД и колонкой из курсора (первый и второй параметр соответственно). В нашем случае названия колонок курсора соответствуют названиям колонок таблицы БД. Это значит, что второй параметр (имя колонки курсора - SourceColumn) заполнять не обязательно. По умолчанию ascCursorChanger подставляет в этот параметр имя колонки из курсора. Чуть сложней с ключевыми колонками. При операциях Update и Delete, объекту ascCursorChanger просто необходимо знать список ключевых колонок (иначе ascCursorChanger не сможет однозначно идентифицировать изменяемую или удаляемую запись). При операции Insert в принципе не надо знать, какие из колонок ключевые, а какие - нет. Но зачастую для ключевых колонок надо сгенерировать уникальное значение, ключевая колонка может быть автоинкрементального типа, значение ключевых полей может формироваться триггером (в двух последних случаях сгенерированные на сервере значения должны быть после вставки считаны с сервера и возвращены клиенту). За то, как формируется значение в описываемой колонке, отвечает параметр InsertValueSource. Он имеет тип ASC_CHANGER_INSERT_VALUE_SOURCE,  его возможные значения описаны в табл. № 3. Обратите внимание, если в этом параметре будет стоять значение, отличное от acivsFromChanges, то приходящее с клиента значение всегда будет игнорироваться. В нашем примере ключевое поле заполняется пользователем, то есть в этом параметре необходимо указать значение acivsFromChanges, но это не стандартный случай. Обычно применяются значения acivsFromIdentity или acivsFromNewID, а остальные применяются в настолько экзотических случаях, что не заслуживают особого разбора (по крайней мере, в этой статье). Значение acivsFromChanges оставим значением по умолчанию (неключевые поля, которых обычно большинство, чаще всего используют именно это значение, поэтому у нас нет необходимости заполнять этот параметр).

Итак, мы описали уже три параметра метода InsertSQLParamAdd, и логично было бы продолжить это описание, но прежде стоит дать полное описание этого метода. Вот оно:

HRESULT InsertSQLParamAdd( 
   [in] BSTR Name, 
   [in, optional] VARIANT SourceColumn, 
   [in, optional, defaultvalue = 0] ASC_CHANGER_INSERT_VALUE_SOURCE InsertValueSource,
   [in, optional, defaultvalue = 0] ASC_CHANGER_INSERT_DEFAULT_VALUE_SOURCE InsertDefaultValueSource,
   [in, optional] VARIANT DefaultValue, 
   [in, optional, defaultvalue = 0] ASC_PARAM_IN_OUT_TYPE InOutType, 
   [in, optional, defaultvalue = VARIANT_FALSE] VARIANT_BOOL IsKey, 
   [out, retval] IascInsertParameter** pParameter 
);

или

Function InsertSQLParamAdd( 
   ByVal Name As String, 
   ByVal SourceColumn As Variant, 
   [ByVal InsertValueSource As ASC_CHANGER_INSERT_VALUE_SOURCE = 0],
   [ByVal InsertDefaultValueSource As ASC_CHANGER_INSERT_DEFAULT_VALUE_SOURCE = 0], 
   [ByVal DefaultValue As Variant], 
   [ByVal InOutType As ASC_PARAM_IN_OUT_TYPE = 0], 
   [ByVal IsKey As Boolean = False]) As IascInsertParameter

Следующий параметр - InsertDefaultValueSource, определяет источник данных в случае, когда для описываемой колонки основной источник (InsertValueSource) задан как acivsFromChanges и данные не были внесены клиентом. Тип параметра InsertDefaultValueSource - ASC_CHANGER_INSERT_DEFAULT_VALUE_SOURCE. Его значения описаны в табл. № 4.

Параметр DefaultValue используется, если в параметре InsertValueSource указано acivsFromDefault или acivsFromString. Если указано acivsFromString, то значение параметра DefaultValue преобразовывается в строку, и уже строка подставляется в SQL (acivsFromString неприменим, если для записи данных используется не генерация SQL-запросов, а применяется прямая модификация на базе ISAM-методов).

Параметр DefaultValue используется также, если в параметре InsertDefaultValueSource указано acidvsFromDefault или acidvsFromString. Замечания и условия, описанные для случая с InsertValueSource = acivsFromString справедливы и для этого случая.

Параметр InOutType типа ASC_PARAM_IN_OUT_TYPE используется для указания, надо ли возвращать клиенту значение описываемого поля. Значение поля может измениться в процессе обработки других параметров этого же поля или из-за действий триггера БД. Описание значений, допустимых в этом параметре, приведены в табл. № 5.

Таблица № 4

 Описание для перечисления ASC_CHANGER_INSERT_DEFAULT_VALUE_SOURCE

Значение

Описание

acidvsOptional (используется по умолчанию)

Поле не обязательно должно быть заполнено. Если в массиве изменений значение поля не задано, это поле просто не включается в SQL-оператор, модифицирующий данные (Insert, Update).

acidvsFromDefault

Если в массиве изменений значение поля не задано, его значение будет браться из параметра DefaultValue.

acidvsRequired

Значение обязательно. Если значение поля не задано, будет возбуждена ошибочная ситуация.

acidvsFromKey

Если в массиве изменений значение поля не задано, то его значение будет взято из ключевых полей.

acidvsFromString

Если в массиве изменений значение поля не задано, его значение будет взято из строки, лежащей в параметре DefaultValue.

acidvsFromUserData

Если в массиве изменений значение поля не задано, его значение будет взято из свойства UserData курсора (см. табл. № 3 — avspFromUserData).

acidvsFromNewID

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

Таблица № 5

Описание для перечисления ASC_PARAM_IN_OUT_TYPE

Значение

Описание

piotInOnly

Значение поля никогда не возвращается клиенту.

piotOutAlways

Значение поля всегда возвращается клиенту.

piotOutIfNoValue

Значение поля возвращается клиенту, только если с клиента в массиве изменений не прибыло значение.

Наконец, последний параметр метода IsKey. Он имеет тип данных Boolean. С помощью этого параметра можно указать, является ли описываемое поле ключевым. 

Этот метод возвращает объект IascInsertParameter (описатель поля).

Второй метод, использованный нами для описания таблицы - UpdateSQLParamAdd. Как вытекает из его названия, он используется для описания полей в таблице БД для операции Update. Можно сказать, что параметры этого метода практически те же, что и у метода InsertSQLParamAdd. Исключение составляют имена перечислений и параметр Flag, имеющий тип ASC_CHANGER_UPDATE_FLAG. (см. табл. № 6).

Есть разница и в интерпретации некоторых параметров методов UpdateSQLParamAdd и InsertSQLParamAdd. 

Таблица № 6

Описание для перечисления ASC_CHANGER_UPDATE_FLAG

Значение

Описание

acufNormal

Обычное поле

acufKey

Ключевое поле. Используется для идентификации строки при ее модификации.

acufKeyReadOnly

Ключевое поле, не подлежащее редактированию на клиенте. Используется для идентификации строки при ее модификации. Данное поле не может быть отредактировано. Если колонка курсора назначена на поле, имеющее этот флаг, а вы используете эту колонку в описании другой таблицы, то вы обязаны задать этот флаг и для поля другой таблицы.

Третий метод - DeleteSQLParamAdd - предназначен для описания полей таблицы при операции удаления (Delete). Вот его описание:

HRESULT DeleteSQLParamAdd(
   [in] BSTR KeyColumnName,
   [in, optional] VARIANT SourceColumn,
   [in, defaultvalue(sptFromSource)] ASC_SQL_PARAM_TYPE Type,
   [out,retval] IascDeleteParameter ** pIascDeleteParameter
); 

или

Function DeleteSQLParamAdd(
   KeyColumnName As String, 
   [SourceColumn], 
   [Type As ASC_SQL_PARAM_TYPE = sptFromSource]
) As IascDeleteParameter 

Параметр KeyColumnName предназначен для определения ключевых полей. Остальные параметры аналогичны параметрам InsertSQLParamAdd.

Методы InsertSQLParamAdd, UpdateSQLParamAdd и DeleteSQLParamAdd предназначены для сокращения описания полей соответствующих операций. Для настроек отдельных атрибутов описания полей можно воспользоваться списками Insert, Update и Delete.

После настройки объекта ascCursorChanger его необходимо подключить к курсору. Это можно сделать в методе Execute объектов ascServer, IascSession или IascCommand, а также подключить к объекту IascCommand при его создании. Объект IascCommand с подключенным ascCursorChanger'ом автоматически подключает его к каждому создаваемому через него курсору, если, конечно, в методе Execute не будет задан другой ascCursorChanger. Последним способом мы и воспользуемся для того, чтобы подключить ascCursorChanger и тем самым сделать наш курсор редактируемым.

Внесите изменение в строке:

        Set cmd = CurSes.CreateCommand()

метода GetCustomersListByCompanyNameMask класса Customers, так чтобы он выглядел следующим образом:

        Set cmd = CurSes.CreateCommand(Changer, 1) 

Вот, собственно, и все... Скомпилируйте NorthWind.DLL и, чтобы проверить редактирование, откройте и запустите на выполнение проект VNorthWind. Внесенные изменения можно сохранить, выбрав из контекстного меню пункт Save.  Да, если при попытке скомпилировать вам выдадут сообщение:

то просто откройте MTS Explorer/Component Services Manager и скажите "shut down" приложению NorthWind.

 

Расширенный механизм записи изменений

Для управления механизмом записи изменений можно применять механизм внедрения в процесс записи. Для этого вместо объекта записи изменений объекту команды IascCommand необходимо подключить любой другой, реализующий метод ascSaveCursorData. Его описание приведено ниже:

 Function ascSaveCursorData( 
    ByRef UserData As Variant, 
    ByRef CursorID As Long, 
    ByRef pBlobChanges As Variant
) As Variant 

Например, если вы хотите подключить вместо ascCursorChanger'а свой объект, вы должны будете, кроме реализации вышеописанного метода, изменить строку:

    Set cmd = CurSes.CreateCommand(Changer, 1) 

на

    Set cmd = CurSes.CreateCommand(Me, 1) 

Примечание для C/C++ программистов: Me - это указатель на объект, доступный в методах этого объекта (аналог this в C++).

После этого в момент записи у вашего объекта будет вызван метод ascSaveCursorData, одним из параметров которого вам будет передан массив изменений. Напрямую этот массив использовать не удастся. Вместо этого вы можете подключить его к ascCursorChanger, и через его методы получить доступ к массиву изменений.

Чтобы подключить массив изменений к ascCursorChanger, надо в ascSaveCursorData вызвать метод AttachChanges объекта ascCursorChanger и передать ему в качестве параметра массив изменений.

ascCursorChanger представляет массив изменений как двумерный массив. По первой оси перечисляются измененные записи, а по второй - колонки.

У каждой записи есть тип изменения (Insert, Update, Delete). Измененные поля содержат эти самые изменения, а неизмененные - значение Empty (VT_EMPTY).

Вдоволь навозившись с ascCursorChanger'ом, вы можете произвести запись в БД вручную (отдельными SQL-вызовами) или вызвать метод ascSave у объекта ascCursorChanger.

Ниже приведен код метода ascSaveCursorData:

'Метод записи изменений пользовательского объекта
Function ascSaveCursorData(UserData, CursorID As Long, BlobChanges)
   Dim Changer As ascCursorChanger
   'Подключение массива изменений к объекту ascCursorChanger.
   Changer.AttachChanges UserData, BlobChanges
   'Доступ к списку изменений (изменение первой ячейки первой записи).
   Changer.Changes(1)(1) = "Some Data"
   'Запись модифицированных измененений в БД.
   Changer.ascSave
End Function

Внутри метода ascSaveCursorData можно переопределять Pre и PostSQL-выражения, разрешить или запретить запись отдельных ячеек, устанавливая свойства колонок IascUpdateParameter.Flag и другие свойства, влияющие на процесс записи изменений в БД. Существует способ включать и отключать возможность записи изменений. Это можно сделать, реализовав в подключенном объекте метод ascCanSaveCursorData.

Примечание для С/С++ программистов: если вы хотите воспользоваться преимуществами методов ascSaveCursorData и ascCanSaveCursorData, не забудьте, что интерфейс, в котором вы их реализуете должен быть dispinterface'ом или dual-интерфейсом (наследоваться от IDispatch). Вызов происходит по имени, то есть сначала вызывается IDispatch::GetIDsOfNames, а потом - IDispatch::Invoke, которому передается полученный от GetIDsOfNames DISPID.

На клиентском берегу

Если вы выполнили все описанные до этого действия, то у вас есть два проекта. Первый реализует полнофункциональный middle-tire компонент, способный через свои методы возвращать ascDB-курсоры. Второй же содержит пару-другую малозначимых строк кода и три ActiveX-компонента. Практически всю работу по подключению удаленного компонента мы выполнили визуальными средствами. Если бы перед нами стояла задача быстро создать middle-tire-проект взаимодействующей с БД, то упрощения на клиентской стороне были бы большим достоинством... Но мы не ищем легких путей! В наши планы входит благое желание подробного и всестороннего издевательства над читателями. Так что с каменно-садистским выражением лица начнем обсуждение возможностей, которые предоставляет нам клиентская часть библиотеки ascDB.

Первое что мы рассмотрим - работа с ascDB-курсорами из кода программы. Для чего это надо? Ну, это не вопрос. Закат солнца вручную - любимое занятие русского человека. Хотя про русских я, наверное, перегнул. Эта особенность просто присуща всем людям в России, она настолько тесно интегрировалась с повседневностью, что стала национальной традицией. Если выделять группу не по национальному признаку, а по принадлежности к профессии, то на первом месте без сомнения будут стоять программисты. Причем, скорее всего они будут не стоять, а суетливо заниматься сборкой гиперкибернетического велосипеда. Причины этого банальны, ведь производимый вручную процесс легче контролировать. Да, ведь совершенно понятно, что я (программист) смогу сделать этот процесс лучше, быстрей и с меньшими затратами (процессорного времени, памяти и т.п.).

Поэтому начнем понемногу.

Сам объект просто создается штатными методами языка, на котором вы работаете. Для VB это - CreateObject или оператор new, для C++ CoCreateInstance[Ex]. Вызов метода также ничем не отличается от вызова любого другого метода. Перед вызовом надо только объявить переменную типа ascCachedCursor, в которую и будет возвращен курсор. Назовем ее Cursor. Передача ascCachedCursor'a на физическом уровне осуществляется при помощи custom marshaling'а. Т.е. по сети передается не ссылка на объект ascCachedCursor, а данные необходимые для "умного" управления курсором.

После вызова в клиент получит первый блок данных (в бинарном виде), метаинформация, описывающая курсор и его колонки, а также указатель на скрытый удаленный объект, с помощью которого в дальнейшем будет происходить подкачка следующих блоков данных и передача данных для записи на сервер. Все это звучит пугающе, но иначе нельзя организовать производительную и удобную работу в распределенной среде. Но не пугайтесь все эти тонкости скрыты от конечного пользователя. Он работает с объектом ascCachedCursor (интерфейсом IascCachedCursor для C/C++-программистов). ascCachedCursor позвляет работать с данными в простом навигационном стиле, как будто его данные расположены на локальной машине. Навигационный стиль означает, что данные курсора представляются как двумерный массив, доступ к элементам которого осуществляется построчно. То есть, для считывания полей некоторой записи необходимо предварительно переместиться на нее с помощью одного из методов навигации (см. табл. № 7). Все методы навигации имеют префикс Fetch. Остальные методы объектов ascCachedCursor и ascVisualCursor тоже имеют свои префиксы. С помощью этих префиксов методы разбиты на несколько логических групп. Такое разбиение позволяет быстро находить нужный метод с помощью complete word в VB, VС++, Delphi и т.п.

Чтобы считать данные из конкретного поля, надо выбрать необходимую колонку и считать значение. Выбрать колонку можно с помощью свойства Columns. Это свойство представляет собой списка (collection). Элемент этого списка является описанием колонки. В качестве идентификатора колонки можно использовать ее имя или порядковый номер. Одно из свойств колонки (под названием Value) позволяет получить данные колонки для текущей строки. Говоря более строго, свойство Value возвращает не само значение, а объект, позволяющий манипулировать значением колонки, но, так как у этого объекта есть свойство по умолчанию, возвращающее Variant - "AsVariant", то с некоторой натяжкой можно сказать, что свойство Value возвращает значение. Зачем же свойство Value возвращает какой-то объект, а не значение, запакованное в Variant? Да просто такое решение позволяет получить не абстрактный Variant, а значение, приведенное к необходимому программисту типу. Для того, чтобы получить значение, приведенное к некоторому типу, можно воспользоваться одним из свойств AsXXX. XXX - это имя необходимого типа данных. Преобразование типа самим кэширующим курсором происходит значительно быстрее, чем конвертация в вариант и обратно. В VB для упрощения написания кода можно вообще не указывать свойство Value, так как у колонки имеется скрытое свойство _Value, использумое по умолчанию.

Таблица № 7

 Описание методов навигации объектов ascCachedCursor и ascVisualCursor

Значение

Описание

FetchFirst

Переместиться на первую запись.

FetchLast

Переместиться на последнюю запись.

FetchNext

Переместиться на следующую запись.

FetchPrev

Переместиться на предыдущую запись.

FetchRandom

Переместиться на произвольную запись, номер которой указан в параметре RowNum.

FetchRelative

Переместиться на запись, относительный номер которой указан в параметре RowNum. Например, если текущая запись имеет № 5 то вызов FetchRelative(-3) переместит курсор на запись № 2, а FetchRelative(2) - на запись № 7.

FetchFreezePos

Все функции навигации приводят к изменению позиции курсора. Иногда необходимо прочитать данные, но не изменять позиции курсора. Например, этим методом пользуется ascGrid, когда выводит строки на экран. Этот метод имеет параметр Freeze типа Boolean, позволяющий «заморозить» или разморозить позицию курсора. В замороженном состоянии не посылается ни одного события Scroll, что и приводит к впечатлению неизменности позиции курсора.

Давайте создадим пример, получающий данные из курсора и помещающий их в текстовое поле. Для этого добавьте к нашей форме новую кнопку. Назовите ее "pbFillList". После этого добавьте новое текстовое поле ("Text2") и установите у него свойства MultiLine и ScrollBars в True и vbBoth соответственно. Создайте (двойным щелчком) событие "..._Click" для новой кнопки и внесите в него следующий код:

Private Sub pbFillList_Click()
    Dim RemObj As Object
    Dim Cursor As New ascCachedCursor
    Set RemObj = CreateObject("NorthWind.Customers")
    RemObj.GetCustomersListByCompanyNameMask Cursor, "%"
    Dim col As IascColumn
    Dim sText As String
    Do
        Dim sLine As String
        sLine = Empty
        For Each col In Cursor.Columns
            If sLine <> Empty Then sLine = sLine + vbTab
                sLine = sLine & col.Value '.AsVariant
        Next
        sText = sText & sLine & vbCrLf
    Loop While Cursor.FetchNext() = rsrcOk
    Text2 = sText
End Sub 

Результат ваших действий должен быть похож на рис. 10.

Вообще каждый из объектов, входящих в библиотеку ascDB, довольно-таки сложен. Практически каждый из них достоин описания в отдельной статье, но, благодаря продуманным значениям по умолчанию параметров методов и свойств, даже начинающий VB-программист сможет использовать их без подготовки. Так происходит и с объектом колонки. С одной стороны, он позволяет легко получить или установить значение колонки. С другой, позволяет получить (и задать) большое количество информации о колонке и данных. Вы можете даже добавить собственную колонку, не связанную напрямую с данными курсора. Кроме описания колонки можно задать/узнать шапку (Caption), визуальные свойства и так называемые расширенные (пользовательские) свойства. Визуальные свойства?! Но какие могут быть у колонок не визуального кэширующего курсора визуальные свойства? А практически все, которые могут быть у поля, выводимого в визуальном компоненте типа ascDataField и ascGrid.

Рис. 10. Текстовое поле, заполненное программным способом.

Естественно, что использовать их в коде можно только для считывания и установки, но вы можете подключить к кэширующему курсору один из перечисленных визуальных компонентов, и они в автоматическом режиме отобразят все настройки визуальных свойств. Программно визуальные свойства доступны через свойство VisualProp объектов ascCachedCursor, ascVisualCursor. В таблице № 8 приводится список поддерживаемых на сегодня визуальных свойств. Если какое либо свойство не задано, и не указано, что его значение надо брать от родительского объекта, то визуальные компоненты используют значения по умолчанию. Значения по умолчанию соответствуют значениям, заданным в Control Panel\Regional Settings и в Control Panel\Display\Appearance.

То, что визуальные свойства можно изменять не только в визуальных компонентах, но и в ascCachedCursor и ascVisualCursor, позволяет централизовать обработку визуального представления данных. Централизация не принуждает делать весь мир зачесанным под одну гребенку. Вы можете определить любое визуальное свойство не только в ascCachedCursor и ascVisualCursor, но и в любом компоненте визуализации (ascGrid, ascDataField или ascComboBox). Но все сказанное выше о свойствах колонок меркнет по сравнению с тем, что, во-первых, вы можете задавать все эти свойства еще на сервере, непосредственно после создания курсора и перед его возвращением клиенту, а во-вторых, все (!!!) без исключения визуальные свойства колонок можно изменять динамически. Например, у вас есть поле типа many, и вы хотите отображать отрицательные числа красным цветом, а числа, превышающие десять тысяч, синим. Для этого вам необходимо подключится к событию "AfterDataRead" любого объекта, реализующего колонки (ascCachedCursor, ascVisualCursor, ascGrid, ascDataField или ascComboBox) и написать код, изменяющий визуальные свойства колонки, основываясь на значении подопытного поля. Событие AfterDataRead вызывается после перехода (Fetch) на любую строку. Перед отрисовкой строки любой компонент визуализации должен перейти на отрисовываемую строку. По умолчанию любой переход на другую строку в курсоре вызывает события AfterScroll и BeforeScroll (перед и после совершением перехода соответственно). Но компоненты типа ascGrid в момент отрисовки, перед тем как сделать переход на отрисовываемую строку, включают так называемый режим заморозки сообщений перемещения (запрещает рассылку AfterScroll и BeforeScroll), тем самым не мешая основной логике программы. Сообщение же AfterDataRead посылается вне зависимости от того, заморожены сообщения перемещения или нет. В общем и целом, динамическая модификация визуальных свойств позволяет упростить создание приложений, имеющих дружественный пользовательский интерфейс, не прибегая к таким сложным способам, как ручная отрисовка ячеек, а централизованность обработки позволяет упростить создание единого стиля в ваших приложениях.

Вторым интересным моментом является то, что для каждой колонки в курсоре можно назначить неограниченное (вернее, ограниченное только здравым смыслом) количество расширенных (пользовательских) свойств. Пользовательское свойство - это некоторое свойство, имеющее имя и значение типа Variant. Расширенные свойства доступны через свойство ExtProp объекта IascColumn. ExtProp имеет строковый индекс, который и является именем свойства. Попытка получить значение незаданного свойства возвратит Empty. Чтобы очистить ранее заданное свойство, ему тоже надо просто задать значение Empty. Расширенные свойства можно задавать на любой стадии жизни курсора (на сервере, в кэширующем и визуальном курсоре, в любом компоненте визуализации). Если свойство задано на сервере, то очистить его уже не удастся. Можно только перебить его значение в дочерних компонентах (ascCachedCursor, ascVisualCursor, ascGrid, ascDataField или ascComboBox). Если значение перебито на некотором уровне, то его очистка приведет к тому, что значение будет браться с предыдущего уровня.

Таблица № 8

Визуальные свойства колонок 

Значение

Тип

Описание

Width

Long

Ширина колонки в пикселях.

WorldWrap Boolean Флаг ативизации режима для переноса слов. Используется в многострочных редакторах.
PainterID String Идентификатор визуального ActiveX-компонента, используемого для отрисовки  поля.

EditorID

String

Идентификатор визуального ActiveX-компонента, используемого для редактирования поля.

Align

Enum

Тип выравнивания (вправо, влево по центру).

ForeColor

Color

Цвет текста.

BackColor

Color

Цвет фона.

Font

IFontDisp

Шрифт.

Format

Enum

Тип формата. Позволяет задать один из предопределенных форматов вывода на экран или указать, что будет использоваться маска формата, заданная в свойстве FormatString. Предопределенные форматы берутся из настроек (Control Panel\Regional Settings) текущего пользователя.

FormatString

String

Строка формата вывода на экран. Используется, если в свойстве Format задано значение «fmtCustom».

DomainName

String

Имя пользовательского типа данных, заданного для этого поля. Аналогичен домену в СУБД или typedef’у в C/C++. Используется для задания дополнительных свойств колонки. Свойства самого домена можно задать через специальный сервисный компонент.

DisplayMode

ASC_COLUMN_DISPLAY_MODE

Тип отображения колонки. Возможно три типа отображения: обычный режим, фиксированный, скрытый.

FromParent

Boolean, с индексом

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

CanRequest

Boolean, с индексом

Это свойство позволяет узнать, можно ли считывать значения определенных визуальных свойств. Свойство доступно только для чтения.

Таблица № 9

Свойства "inf" объектов ascCachedCursor и ascVisualCursor

Значение

Описание

infCountRow

Возвращает количество строк в курсоре. Его значение недоступно, если курсор открыт в однонаправленном режиме, и еще не считаны все строки. Вместо свойства infCountRow можно воспользоваться свойством infCountRowsRead.

infCountRowsRead

Возвращает количество уже считанных строк.

infCurrentRow

Возвращает порядковый номер выделенной строки.

infReadOnly

Позволяет узнать, можно ли редактировать курсор. Например, это свойство устанавливается в True, если к курсору не был подключен объект ascCursorChanger.

infRowChangeType

Позволяет узнать тип изменений строки (rctInserted, rctInserting, rctUpdated, rctDeleted) или сообщает, что строка не изменялась (rctNotChanged).

infRowError

Возвращает объект, описывающий ошибку, произошедшую при записи изменений в БД для текущей строки.

infSaveErrors

Список всех ошибок произошедших при записи изменений в БД.

infState

Возвращает состояние, в котором находится курсор.

Я уже много рассказал о расширенных свойствах, но еще не сказал, зачем их можно использовать. Отчасти, это объясняется тем, что каждый из вас может найти самостоятельное применение расширенным свойствам. Но одно специализированное применение все же есть. Он связано с редакторами и отрисовщиками (о которых речь шла ранее). Через расширенные свойства редакторы и отрисовщики могут получать расширенную информацию. Например, нами реализован редактор, который позволяет выбирать элемент из списка и помещать в ячейку ассоциированное с ним значение. То есть, говоря проще, задавать список для редактора, имеющего вид выпадающего списка. Если вы намерены создавать собственные редакторы, то тоже можете воспользоваться расширенными свойствами.

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

Если на сервере при создании курсора не были жестко заданы такие параметры, как тип курсора и размер сетевого буфера (количество строк, считываемых за одно обращение по сети), то их можно задать непосредственно при вызове метода, возвращающего курсор. Для этого необходимо установить соответствующие свойства (rsDBCursorType и rsFetchBufferSize) курсора. При этом курсор занесет необходимую информацию в буфер маршалинга, а на стороне сервера при вызове метода Execute информация будет считана и использована. В случае использования визуального курсора все это можно сделать визуально. Следующим интересным свойством объектов ascCachedCursor и ascVisualCursor является rsUpdateMode. Оно позволяет определить событие, на которое будет происходить запись изменений (формирование буфера и посылка его на сервер). Поддерживаются три значения: cumCell - запись происходит при окончании редактирования ячейки, cumRow - запись происходит при сходе со строки, cumSave - запись происходит только при ручном вызове метода Save. Выбрав cumCell или cumRow, вы можете избавиться от необходимости следить за сохранением изменений в БД, а выбрав cumSave можно с оптимизировать сетевой трафик, посылая все изменения за один сетевой вызов.

Свойства с префиксом "inf" позволяют получить информацию о курсоре. Их список приведен в табл. № 9.

Следующей группой свойств являются свойства с перфиксом "Is". Они перечислены в табл. № 10.

Таблица № 10

Свойства "Is" объектов ascCachedCursor и ascVisualCursor

Значение

Описание

IsEmpty

Возвращает True, если курсор пуст (не содержит ни одной записи).

IsEOF

Возвращает True, если позиция курсора находится за последней строкой.

IsFirst

Возвращает True, если позиция курсора находится на первой записи.

IsLast

Возвращает True, если позиция курсора находится на последней записи.

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

События кэширующего и визуального курсоров

Для того, чтобы взаимодействие с кэширующим или с визуальным курсором могло быть интерактивным и простым, в них определен набор событий. Все события имеют один из трех префиксов: Before (вызывается перед некоторым действием), After (вызывается после некоторого действия) и On (вызывается непосредственно во время выполнения некоторого действия). Все Before-события имеют параметр, позволяющий отменить текущее действие. Список событий объектов ascCachedCursor и ascVisualCursor приведен в табл. № 11.

Таблица № 11

События объектов ascCachedCursor и ascVisualCursor

Значение

Описание

AfterCancel

Вызывается после отмены введенных изменений.

AfterChangeCell

Вызывается после изменения ячейки.

AfterClose

Вызывается после закрытия курсора.

AfterDataRead

После перехода на другую запись (перед их отображением).

AfterDelete

Вызывается после удаления строки.

AfterInsert

Вызывается после вставки строки.

AfterInsertCancel

Вызывается после отмены вставки строки.

AfterOpen

Вызывается после открытия курсора.

AfterRowsRead

Используется совместно с однонаправленным курсором. Вызывается после считывания с сервера нового буфера. В этот момент становится доступна информация о новом количестве строк в курсоре.

AfterSave

Вызывается после записи данных в БД.

AfterScroll

Вызывается после операции прокрутки. Примечание: Прокрутка может произойти из-за действий пользователя, программного перемещения позиции в курсоре методами FetchXXX или при открытии курсора. Это сообщение не вызывается, если был вызван метод FetchFreezePos с параметром True.

BeforeCancel

Вызывается перед отменой введенных изменений.

BeforeChangeCell

Вызывается перед изменением ячейки.

BeforeClose

Вызывается перед закрытием курсора.

BeforeDelete

Вызывается перед удалением строки.

BeforeEdit

Вызывается перед редактированием ячейки.

BeforeInsert

Вызывается перед вставкой строки.

BeforeOpen

Вызывается перед открытием курсора.

BeforeSave

Вызывается перед записью данных в БД.

BeforeScroll

Вызывается перед операцией прокрутки (см. примечание к событию AfterScroll).

OnCalculate

Позволяет произвести вычисления необходимых значений нарастающим итогом и занести эти значения в пользовательские колонки. Это событие доступно при условии, что включен режим rscmBatch (свойство rsCalcMode), свойство rsDBCursorType установлено в ascCUR_FORWARD и включено кэширование (свойство rsCache).

AfterCreate Вызывается перед окончанием создания

BeforeCreate

Вызывается после окончания создания. В этот момент курсор готов к работе и контейнер (например, VB) готов к рассылке сообщений. Это сообщение необходимо, так как если на Form_Load вызвать какой-нибудь метод объектов ascCachedCursor или ascVisualCursor, соответствующие события не будут вызваны, поскольку контейнер еще не готов к рассылке событий.

OnError

Вызывается в момент возникновения ошибки. Позволяет по-своему обработать возникшую ошибочную ситуацию.

OnInitRow

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

 

Использование событийного аппарата кэширующего и, главное, визуального курсоров дает вам удобный и простой способ контроля как за свойствами самого курсора, так и за данными, управляемыми им.

Расширим немного наш тестовый проект. Добавим CheckBox с именем "cbVCursorActive". Он будет отражать состояние визуального курсора и позволит нам изменять его состояние. Вторым этапом сделаем так, чтобы все нечетные строки имели светло-желтый цвет. Третьим шагом добавим пользовательскую колонку и заполним ее на сообщение OnCalculate порядковым номером строки. После записи вызовем пересчет значений, чтобы они не теряли своей актуальность из-за сдвига при вставке новых строк.

Чтобы все перечисленное выше заработало, добавьте обработчики событий:

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

'Эта переменная необходима нам для подсчета
'строк нарастающим итогом
Dim RowCnt As Long
Private Sub ascVisualCursor1_AfterDataRead()
   Dim col As IascColumn
   'Если строка не четная...
   If ascVisualCursor1.infCurrentRow Mod 2 = 0 Then
      'то устанавливаем у всех колонок белый цвет фона...
      For Each col In ascVisualCursor1.Columns
         col.VisualProp.BackColor = vbWindowBackground
      Next
   Else
      'иначе у всех колонок желтый цвет фона
      For Each col In ascVisualCursor1.Columns
         col.VisualProp.BackColor = &H80000018
      Next
   End If
End Sub
Private Sub ascVisualCursor1_AfterOpen()
   'Сбрасываем калькулируемую переменную в ноль
   RowCnt = 0
   'На открытие курсора устанавливаем соответствующим
   'образом переключатель cbVCursorActive
    cbVCursorActive.Value = Abs(True)
    Dim col As IascColumn
   'и добавляем новую колонку "MyCol"
    With ascVisualCursor1.Columns
      'По умолчанию колонок нет. То есть они есть, но
      'помечены как Default. При этом добавление первой
      'колонки приведет к очистке Default'ных и наша
      'новая колонка останется одна...
      'Чтобы этого не случилось надо установить свойство
      'колонок Default в False. Это превратит колонки
      'в обычные, как если бы они были добавлены в
      'коде программы и после добавления новой колонки они
      'не исчезнут.
      .Default = False
      'Теперь можно смело добавлять новую колонку...
      .Add Name:="MyCol", Caption:="Моя колонка", Type:=ASC_CDT_LONG
   End With
End Sub
Private Sub ascVisualCursor1_AfterClose()
    'На закрытие курсора выключаем переключатель...
    cbVCursorActive.Value = Abs(False)
    'и уничтожаем колонки. Для этого переводим их
    'в состояние Default
    ascVisualCursor1.Columns.Default = True
End Sub
Private Sub ascVisualCursor1_OnCalculate()
   'Производим расчеты...
   'Можно было бы просто присвоить полю ascVisualCursor1.infCurrentRow,
   'но ведь мы не ищем простых путей...
   'Режим rsCalcMode = rscmBatch гарантирует, что событие
   'OnCalculate будет вызвано последовательно для всех считываемых строк
   RowCnt = RowCnt + 1
   ascVisualCursor1.Columns("MyCol").Value.AsLong = RowCnt
End Sub
Private Sub ascVisualCursor1_AfterSave()
   'Сбрасываем калькулируемую переменную в ноль
   RowCnt = 0
   'После записи
   ascVisualCursor1.ReCalculate
End Sub
Private Sub cbVCursorActive_Click()
   'При переключении cbVCursorActive переводим в
   'соответствующее состояние визуальный курсор
    ascVisualCursor1.ascActive = Abs(cbVCursorActive)
End Sub

После того, как вы измените код, необходимо изменить некоторые настройки курсора. Во-первых, переведите визуальный курсор в неактивное состояние. Для этого переведите его свойство ascActive в False. Во-вторых, установите свойство rsCalcMode в rscmBatch а свойство rsDBCursorType в actForward. Это включит режим калькуляции. Все, можете перевести свойство ascActive в True, нажать F5 и полюбоваться своей работой. Прокрутите табличный компонент вправо. Самая последняя колонка - та самая, которую мы добавили на открытие курсора. У вас должно получиться нечто, похожее на рис. 11.

Рис. 11. Так должна выглядеть форма нашего приложения после модификации

Добавить колонку можно и из страницы свойств "колонки" объекта ascVisualCursor1.

Наборы сервисных компонентов

Для упрощения и ускорения работы в ascDB имеется ряд так называемых сервисных компонентов. Как правило, они разбиты на группы. Такая группа состоит из двух компонентов: серверного (выполняющего основную работу) и клиентского (ActiveX-элемента, реализующего GUI). На сегодня доступны три такие группы. Эти группы перечислены в табл. № 12.

Таблица № 12

Список групп сервисных компонентов

Компонент

Описание

Edit Manager

Редактор редакторов, позволяющий зарегистрировать новые ActiveX-элементы и визуально настроить соответствие между ними и доменами БД.

Object Loader

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

Command Designer

Позволяет визуально создавать описание команды и ее changer’а. Настроенное описание команд сохраняется в файлах, организованных в специальное хранилище. Эти файлы могут использоваться совместно с ascServer'ом для загрузки полностью настроенной команды по имени описания. Графический интерфейс дизайнера команд доступен в виде ActiveX-элемента и в MMC-консоли.

Визуальный дизайнер команд

Самым интересным из сервисных компонентов является Command Designer. Он предназначен для быстрой и безошибочной настройки команды (IascCommand), а также объекта, производящего запись (ascCursorChanger). Графический интерфейс дизайнера реализован в виде "мастера". Он позволяет шаг за шагом пройти все этапы создания и настройки команды. Кроме этого, с его помощью можно изменять однажды созданные и сохраненные команды. Высокая интерактивность дизайнера команд позволяет не держать никакой информации о БД в голове и точно быть уверенным в работоспособности команды, ведь ее можно протестировать на любом этапе настройки. Вторым достоинством этого инструмента является безопасность, так как все действия происходят в специально открытой транзакции, и вы сами принимаете решение, производить ли запись сделанных изменений.

Итак, как говорилось ранее, создание новой команды разбито на несколько этапов.

  1. Подключение к серверу и выбор источника данных из списка, предоставляемого сервером (на сервере должен быть установлена библиотека "ascRemoteBaseControls"). 
  2. Создание SQL-запроса, и, если есть необходимость, добавление и настройка параметров команды. 
  3. Настройка свойств колонок, визуальных свойств колонок и расширенных (пользовательских). На этом этапе можно настроить пользовательские типы данных и задать редакторы и рисовальщики. 
  4. Следующий этап - настройка ascCursorChanger'а (если в этом есть надобность). Опять-таки все происходит визуально. Вы выбираете таблицы, в которые необходимо производить запись, назначаете свойства для колонок и т.п.

Начиная со второго этапа можно протестировать команду.

Внешний вид дизайнера команд представлен на рис. 12.

Рис. 12. Дизайнер команд

Чтобы загрузить на сервере настроенную команду, дизайнеру команд достаточно одной строчки кода:

Set cmd = srv.LoadCommand("CommandName")

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

Заключение

В рамках одной статьи невозможно не только описать все тонкости и принципы работы с библиотекой ascDB, но и даже полностью описать ее возможности. В следующих публикациях мы подробнее остановимся на разных аспектах использования библиотеки ascDB. А сейчас скажу только, что можно принять участие в закрытом бета-тестировании библиотеки ascDB заполнив форму здесь. Там же можно задать интересующие вас вопросы, узнать о других решениях в области ПО. Если вы хотите написать мне лично, то пишите по адресу mag@rsdn.ru. Телефон (095) 180-0201.

Чистяков В.Ю.


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