Рассмотрим вариант расширения стандартного CADLib API в части выборки объектов с некоторыми параметрами из базы при помощи SQL-запросов на примере получения всех файлов публикации, в которых встречаются объекты коллизий. Подобную задачу мы решим сперва вручную при помощи только лишь методов CADLib API, а затем комбинированным способом, с переносом части логики в SQL-запрос. Весь исходный код доступен на GitHub по ссылке.
1. О приложении CADLib и его API
CADLib — это продукт от CSoft Development, поставляющийся в виде двух частей: библиотека стандартных компонентов, используемая в вертикальных приложениях ModelStudio на базе nanoCAD и AutoCAD, а также для некоторых модулей от Нанософт (например nanoCAD BIM Конструкции) и среда общих данных — для организации совместной работы группы инженеров, проверки модели на коллизии и т. д. В нашей статье мы будем говорить про CADLib как СОД.
С программной стороны, CADLib написан c применением нескольких .NET библиотек; в частности, его API доступно только на .NET. Библиотека компонентов, напротив, как более старый продукт, разрабатывалась на C++ и имеет только C++ (NRX) API, некоторая часть которого выведена для использования из‑под .NET в nanoCAD BIM Конструкции API.
Используемые в API CADLib программные библиотеки располагаются в каталоге установки CADLib, по умолчанию в папке «C:\Program Files (x86)\CSoft\Model Studio CS\Viewer\bin\x64</b>». Как такового SDK к API нет, небольшие материалы от вендора касаются лишь начальных шагов в части создания нового приложения и не содержат конкретики по применению всех методов API.
Проблематика необходимости использовать SQL-запросы связана с низкой скоростью работы стандартного API, особенно на БД, развернутых на PostgreSQL, по сравнению с MS SQL. В данной простой задачке время выполнения запроса через SQL было в 40 раз меньше, чем через API. На более сложных моделях этот разрыв будет намного больше (до нескольких сотен раз).
2. Постановка задачи для нашей статьи
Как мы обозначили во введении, зададимся целью получить перечень файлов публикации, объекты которых фигурируют в коллизиях. Со стороны UI информация о файле публикации содержится в параметре «PROJECT_CHECKIN_FILE_NAME» у объекта коллизии:
Наша задача будет заключаться в переборе имеющихся коллизий, получению у них значений параметров COLLISION_OBJECT1 и COLLISION_OBJECT2; далее — выборке объектов из БД, чьи идентификаторы (UID) соответствуют полученным значениям, из которых предварительно удалены дубли. По окончании для полученных объектов модели мы сформируем перечень значений параметра PROJECT_CHECKIN_FILE_NAME, из которых мы также удалим дубли и сделаем результатом работы запроса.
3. Реализация структуры плагина
Наш плагин будет запускаться из-под отдельного меню на основной ленте команд и иметь 3 команды:
В соответствии с правилами создания своих команд, нам необходимо создать класс Windows.Form и реализовать интерфейс ICADLibPlugin, то есть 3 метода: GetMenu(), GetToolbars(), TrackInterfaceItems(InterfaceTracker tracker). Так как наш плагин будет запускаться из-под меню, то мы уделим внимание только методу GetMenu, а в GetToolbars() сделаем return null. Метод TrackInterfaceItems задает возможность настройки доступности команд для пользователей с определенными правами и при выделении объекта в базе/открытости самой базы для просмотра. Так как второй вариант действия через SQL-запрос будет задействовать метод COPY, то нам потребуются права администратора на выполнение запроса, поэтому в теле метода TrackInterfaceItems мы ставим роль запускающего кнопку как Admin (так как при любой другой роли команда попросту не отработает).
Привяжем к созданным элементам меню соответствующие команды и настроим их отображение:
Скомпилированную dll необходимо будет добавить в автозагрузку CADLib, в файл C:\Program Files (x86)\CSoft\Model Studio CS\Viewer\bin\x64\plugins.xml в виде строки <Plugin name="...CADLib_demo_api_extend_sql\bin\Debug\TBS_CADLib_demo_sql_connector.dll"/> в блоке Plugins.
4. Реализация логики работы только через API
Рассмотрим процесс получения данных только с использованием API без привлечения SQL-запросов. Сразу оговоримся, что этот процесс будет реализован в рамках метода MenuItem_UsingAPI_Click.
Здесь мы сперва получаем перечень объектов из категории «Коллизии», затем для каждой из них запрашиваем значение двух параметров: COLLISION_OBJECT1 и COLLISION_OBJECT2, значение которых пытаемся привести к типу Guid (в теории, должно отработать для всех), и если такие значения отсутствуют во временном списке, добавляем их туда.
Во второй части процесса мы перебираем все объекты БД CADLib и ищем, какие из них имеют данные Guid, и если имеют, то для этих объектов тут же запрашиваем значение свойства для параметра PROJECT_CHECKIN_FILE_NAME, и если он не равен null, то заносим во временный список. По завершении процесса выводим MessageBox со временем исполнения запроса и списком из таких файлов.
5. Реализация логики работы только через SQL
Средствами CADLib API возможен запуск SQL-запросов в двух режимах: через выбор запускаемого файла вручную и автоматически. При этом используемые методы RunSqlScript и RunSqlScripts сильно отличаются по сигнатуре и логике реализации.
5.1 RunSqlScript
Этот метод хоть и возможен, но не рекомендован к использованию, так как конечный пользователь может не знать, какой SQL-запрос за что отвечает. Кроме того, у метода нет обработчика ошибок, и потому его сложно проконтролировать. В качестве аргумента метод принимает объект класса OpenFileDialog, в теле которого можно установить предварительный путь для выбора файла, но сам файл выбирать бессмысленно (указывая OpenFileDialog.FileName предварительно), так как в теле метода все равно происходит открытие диалога выбора файла.
5.2 RunSqlScripts
Этот метод более предпочтителен для программного использования. Конкретные SQL-запросы можно зашить в ресурсы приложения (например, как Embedded resource), далее при вызове соответствующего метода искать в ресурсах проекта одноименные файлы (или содержащие в названии поисковой паттерн), выгружать в локальный файл, сохраняя во временные файлы системы и подавая методу RunSqlScripts). После выполнения запроса SQL-файл можно удалить.
Общим для обоих методов является логика сохранения результата выполнения запроса в БД. Например, результирующую таблицу можно сохранить во временный текстовый файл по фиксированному пути. Примеры таких путей — папка приложения в папке C:\ProgramData или временная папка C:\Windows\Temp. Мы намеренно игнорируем временную папку пользователя, так как имя пользователя в общем случае может содержать символы Кириллицы/спецсимволы, которые приведут к ошибке в SQL-запросе.
Так как запрос может исполняться продолжительное время, то целесообразно предусмотреть функцию, которая будет проверять, завершился ли он. Здесь есть 2 варианта: либо следить, создался ли файл для записи результата по указанному пути (здесь, C:\Windows\Temp\cadlib_output.txt), либо следить за delegate void DReportExchangeProgress.
В качестве результата запроса мы сохраним текстовый файл таблицы (название колонки и значения неравные null с именем файла публикации). После этого считаем файл и выведем результат:
6. Ограничения подхода
Отметим, что указанный подход через SQL-запросы обязан предусматривать возможность эксплуатации плагина для БД на PostgreSQL и MS SQL (так как синтаксис запросов будет разный, как и названия колонок и таблиц на этих СУБД). В нашем сценарии использовался только PostgreSQL. Отметим, что во время выполнения кода можно запросить тип используемой СУБД в виде значения enum EServerType у CADLibrary.Connection.ServerType.
Все SQL-запросы необходимо сперва писать и проверять вручную через десктопные программы обслуживания БД. Например, для PostgreSQL это pgAdmin, а для MS SQL — SQL Server Management Studio.
Также важный момент — это зависимость SQL-запросов от версий CADLib; работоспособность запросов в общем случае не гарантируется для всех версий БД CADLib, особенно PostgreSQL, которую постоянно изменяют для большей стабильности, поэтому плагин следует применять с осторожностью для разных версий БД PostgreSQL и самого CADLib.
Еще одна важная деталь: SQL-запросы могут работать только на извлечение данных. Удалить или изменить имеющуюся таблицу в БД CADLib, относящуюся к проекту, невозможно (запрос пропустит выполнение подобных сценариев без какой-либо ошибки). Это логичная и правильная позиция разработчиков во избежание нарушения целостности структуры БД.
7. Результаты и обсуждение статьи
Ещё раз акцентируем внимание, что весь исходный код данного плагина приведен на открытом репозитории.
Указанную логику мы реализовали для одного из наших плагинов для CADLib и скоро открыто его аннонсируем для всех пользователей CADLib.