При интерактивной работе пользователя в управляемой форме часто возникает необходимость динамически запрашивать данные из другой информационной базы 1С — например, проверять остатки товаров, статус контрагента или цены при выборе строки. Если на каждый серверный вызов устанавливать новое COM-соединение, форма начинает работать крайне медленно из-за задержек на авторизацию и инициализацию контекста внешней базы.
Разберем, почему попытки сохранить COM-объект на сервере между вызовами терпят неудачу, и рассмотрим архитектурно правильные способы решения этой задачи.
Посмотрим на внутреннее устройство клиент-серверной архитектуры 1С:Предприятия. При работе с управляемыми формами каждый вызов с клиента на сервер через &НаСервере или &НаСервереБезКонтекста является независимым (stateless-архитектура). В кластере серверов 1С последовательные запросы одного и того же пользователя могут обрабатываться разными рабочими процессами rphost.
Проанализируем технические ограничения платформы:
COMОбъект представляет собой указатель операционной системы Windows. Платформа 1С не умеет сериализовать такие системные дескрипторы. По этой причине его невозможно сохранить в реквизитах формы, параметрах сеанса или поместить во временное хранилище через метод ПоместитьВоВременноеХранилище().Рассмотрим самое надежное и современное решение: замена тяжелого COM-соединения на веб-технологии. База-источник публикуется на веб-сервере (IIS или Apache), и в ней создается легковесный HTTP-сервис, отдающий остатки номенклатуры в формате JSON.
В отличие от устаревших SOAP Web-сервисов, платформенные HTTP-сервисы 1С обрабатывают запросы быстрее и не создают избыточных XML-оберток. Вызов HTTP-запроса на сервере формы выполняется за доли секунды и не требует постоянного удержания соединения.
Разберем по шагам реализацию запроса на клиенте и сервере текущей базы:
&НаСервереБезКонтекста
Функция ПолучитьОстатокТовараЧерезHTTP(КодНоменклатуры)
Сервер = "api.company.local";
ЗащищенноеСоединение = Неопределено;
Соединение = Новый HTTPСоединение(Сервер, 443, "Логин", "Пароль", , 10, ЗащищенноеСоединение);
Запрос = Новый HTTPЗапрос("/base2/hs/stocks/get?code=" + КодНоменклатуры);
Ответ = Соединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку());
Данные = ПрочитатьJSON(ЧтениеJSON);
Возврат Данные.Остаток;
Иначе
ВызватьИсключение "Ошибка получения остатка: " + Ответ.КодСостояния;
КонецЕсли;
КонецФункции
Проанализируем ситуацию, когда дорабатывать вторую информационную базу и писать собственные HTTP-сервисы запрещено. В этом случае в целевой базе достаточно опубликовать стандартный интерфейс OData.
Платформа 1С автоматически сформирует REST API ко всем объектам и виртуальным таблицам базы. Запрос остатков по конкретной номенклатуре отправляется стандартным объектом HTTPСоединение напрямую к виртуальной таблице остатков регистра накопления. Это избавляет от необходимости инициализировать COM-коннектор и не требует модификации конфигурации второй базы.
Если интеграция обязана работать исключительно через COM-соединение, применим паттерн фонового процесса. Фоновое задание запускается на сервере один раз и непрерывно держит COM-соединение в рамках одного потока.
Выясним логику взаимодействия формы и фонового задания:
ФоновыеЗадания.Выполнить().COMОбъект и запускается цикл ожидания команд.Рассмотрим вариант, упомянутый участниками обсуждения: создание и хранение COM-объекта в модуле управляемого приложения или в переменной формы на стороне клиента. Обратите внимание: этот способ имеет существенные архитектурные ограничения.
Разберем ограничения клиентского COM-соединения:
comcntr.dll.Пример инициализации соединения в переменной модуля управляемого приложения на клиенте:
// В модуле управляемого приложения
Переменная глВнешнееСоединение Экспорт;
Процедура ПриНачалеРаботыСистемы()
#Если Клиент И Не ВебКлиент Тогда
Попытка
Коннектор = Новый COMОбъект("V83.COMConnector");
глВнешнееСоединение = Коннектор.Connect("Srvr=""Server1C"";Ref=""TargetBase"";Usr=""User"";Pwd=""Password"";");
Исключение
Сообщить("Не удалось установить COM-соединение: " + ОписаниеОшибки());
КонецПопытки;
#КонецЕсли
КонецПроцедуры
При таком подходе форма обращается напрямую к переменной глВнешнееСоединение без необходимости каждый раз тратить время на авторизацию во внешней базе.