5 июля 2012 г.

Подключение к серверу приложений. Наборы данных

Перед тем, как перейти к вызову методов сервера приложений, нужно разобраться с форматом, в котором сервер приложений возвращает наборы данных.
Сервер может возвращать наборы данных в двух форматах: внутреннее двоичное представление TClientDataSet или XML. По умолчанию используется первый формат, для использования XML необходимо вызвать метод SetFormat сервера приложений с параметром 'xml'.

Данные в формате XML выглядят следующим образом (результат GetDBProperties):

<?xml version="1.0" encoding="UTF-16"?>
<ROOT>
<fieldset>
<field Id="c0" Name="_PARAMNAME" DataType="string"/>
<field Id="c1" Name="_PARAMVALUE" DataType="string"/>
<field Id="c2" Name="_PARAMTYPE" DataType="int"/>
</fieldset>
<rowset>
<row c0="Checkunique" c1="1" c2="5" />
<row c0="CryptFiles" c1="0" c2="5" />
<row c0="DefaultDir" c1="...CheckOuts" c2="0" />
<row c0="DefaultFileFolder" c1="...Files" c2="0" />
<row c0="DriveLetter" c1="L" c2="0" />
<row c0="GournalSize" c1="500000" c2="1" />
<row c0="GournalTruncPercent" c1="30" c2="1" />
<row c0="Internal" c1="20070405" c2="0" />
<row c0="LastUpdate" c1="23.11.2007" c2="0" />
<row c0="MaximageSize" c1="-1" c2="1" />
<row c0="MaxTextSize" c1="-1" c2="1" />
<row c0="SP" c1="0" c2="0" />
<row c0="UpdateBuild" c1="8.5.2.78" c2="0" />
<row c0="Version" c1="20061001" c2="0" />
<row c0="VersionGroupQuantity" c1="1" c2="1" />
<row c0="WriteLog" c1="1" c2="5" />
</rowset>
</ROOT>

На форуме Аскона один из участников приводил пример использования набора данных в формате XML в .NET (получение из него объекта System.Data.DataSet):

AppServer.SetFormat("xml", out lReturn, out lError);
AppServer.ConnectToDBEx(DBName, "", "", out lReturn,
    out lError);
Result = AppServer.GetDBProperties(out lReturn,
    out lError).ToString();
System.Data.DataSet DS = new DataSet();
System.Xml.XmlDocument XmlDoc = new System.Xml.XmlDocument();
XmlDoc.LoadXml(Result);
DS.ReadXml(new System.Xml.XmlNodeReader(lXmlDoc));

Использование формата XML связано со значительными накладными расходами как на стороне сервера, так и на стороне клиента. Если есть возможность, то эффективнее использовать наборы данных в двоичном формате TClientDataSet. В Delphi сделать это очень просто:

var
    LDataSet: TClientDataSet;
begin
    LDataSet := TClientDataSet.Create(nil);
    try
        LDataSet.Data := AppServer.GetDBProperties();
        while not LDataSet.Eof do
        begin
            ParamName := LDataSet.FieldValues['_PARAMNAME'];
            ParamValue := LDataSet.FieldValues['_PARAMVALUE'];
            LDataSet.Next();
        end;
    finally
        LDataSet.Free();
    end;
end;

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

В Delphi 2009 и более поздних версиях используется кодировка UTF-8 для метаданных, а в предыдущих версия Delphi для метаданных использовалась кодировка ANSI. Так как сервер приложений написан на Delphi 6, он возвращает метаданные в кодировке ANSI, и, чтобы использовать их в новых версиях Delphi, необходимо использовать специальный класс, наследник TClientDataSet. Проблема с кодировкой метаданных обсуждалась в теме Написание плагинов на Delphi 2009 на форуме Аскона.

Итак, нам необходим класс-наследник TClientDataSet, который выполнял бы необходимую обработку метаданных. К тому же, хотелось бы избавиться от постоянного повторения конструкции try...finally при использовании этого класса. Здесь на ум сразу приходит решение создать интерфейс IDataSet для работы с набором данных, и функцию, которая бы возвращала этот интерфейс. Заглядывая немного вперед, мы увидим и еще один плюс данного решения: если с нашей реализацией все-таки возникнут проблемы, то с минимальными переделками мы сможем вынести реализацию этого интерфейса в DLL, написанную на Delphi 6, с точно таким же TClientDataSet, что и у сервера приложений.

И еще подталкивает нас в сторону использования IDataSet тот факт, что такой интерфейс уже описан в библиотеки типов клиентского приложения Лоцмана, и используется в плагинах. Отсюда следует и еще одно преимущество: возможность написания такого кода, который будет без изменения работать как внутри плагина, так и в отдельном клиентском приложении.

Исходный код реализации IDataSet достаточно прост, все методы интерфейса соответствуют таковым у TClientDataSet. Единственное отличие в том, что в реализацию свойства IDataSet.FieldValue добавлена небольшая оптимизация, укоряющая поиск поля по имени. Подробнее можно почитать в статье How Fast Can You FieldByName? (на английском).

Код предназначен для Delphi версий 2009-XE. В XE2 необходимы небольшие правки, связанные с тем, что изменились имена модулей. Если убрать TCompatDataSet, то код можно использовать и в более старых версиях Delphi.

Исходный код реализации IDataSet.

Комментариев нет:

Отправить комментарий