Сервер может возвращать наборы данных в двух форматах: внутреннее двоичное представление 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.
Комментариев нет:
Отправить комментарий