Переход со старых редакций конфигураций (например, с Бухгалтерии для Казахстана редакции 1.5 или 2.0 на обычных формах) на современные управляемые интерфейсы (редакция 3.0 на платформе 1С 8.3) часто преподносит неприятные сюрпризы разработчикам. Один из наиболее болезненных вопросов — это интеграция со сторонними базами данных посредством COM-соединений.
В обычном приложении архитектура позволяла объявить глобальную экспортную переменную в модуле обычного приложения, инициализировать её при запуске или открытии формы списка и использовать на протяжении всего сеанса работы пользователя. В управляемом приложении такая схема полностью разрушается из-за жесткого разделения контекста исполнения на клиент и сервер. Давайте подробно разберем, почему старые методы не работают, и рассмотрим все жизнеспособные варианты решения этой задачи — от простых обходных путей до передовых архитектурных паттернов.
Для начала проанализируем ситуацию, с которой сталкивается разработчик при миграции. В управляемом приложении код форм и интерфейса выполняется на клиенте, а запись документов, проведение и подписки на события — строго на сервере.
Попытки решить задачу старыми методами натыкаются на следующие ограничения платформы 1С 8.3:
ПараметрыСеанса) предназначены для хранения данных на сервере в течение всего сеанса. Однако они могут хранить только строго определенные типы данных, поддерживающие сериализацию. Поместить туда COM-объект (например, V83.COMConnector или результат метода Connect()) платформа не позволит и выдаст ошибку на этапе компиляции или выполнения.ПередЗаписью, ПриЗаписи, ОбработкаПроведения) отрабатывают на сервере в транзакции базы данных. Даже если вы успешно создали COM-соединение на клиенте в форме списка, серверный код подписки физически не имеет доступа к этой переменной.Выясним, как обойти эти ограничения и правильно выстроить работу обмена данными.
Существует интересный обходной путь, позволяющий обойти платформенное ограничение на сериализацию COM-объектов при помещении во временное хранилище. Начиная с версии платформы 1С 8.3, прямая передача COM-объекта во временное хранилище вызывает ошибку: «Переданное значение не может быть помещено во временное хранилище».
Однако разработчики обнаружили лазейку: если поместить COM-соединение внутрь сериализуемого контейнера (такого как Массив или Структура), платформа пропускает эту конструкцию. Рассмотрим пример реализации данного метода.
При открытии формы или выполнении подключения на сервере мы создаем COM-объект, упаковываем его в массив и помещаем во временное хранилище с привязкой к уникальному идентификатору формы:
&НаКлиенте
Перем АдресХранилища;
&НаКлиенте
Процедура ПодключитьКБазе(Команда)
СтрокаПодключения = "Srvr=""ServerName"";Ref=""BaseName"";";
СоздатьСоединениеНаСервере(СтрокаПодключения, АдресХранилища);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура СоздатьСоединениеНаСервере(СтрокаПодключения, АдресХранилища)
// Инициализируем соединитель
Соединитель = Новый COMОбъект("V83.COMConnector");
// Выполняем подключение к внешней информационной базе
Соединение = Соединитель.Connect(СтрокаПодключения);
// Упаковываем в сериализуемый массив для обхода ограничений платформы 8.3
Контейнер = Новый Массив;
Контейнер.Добавить(Соединение);
// Помещаем во временное хранилище с уникальным идентификатором
АдресХранилища = ПоместитьВоВременноеХранилище(Контейнер, Новый УникальныйИдентификатор);
КонецПроцедуры
Для последующего обращения к созданному соединению в рамках серверных вызовов формы мы извлекаем его из хранилища по сохраненному адресу:
&НаСервереБезКонтекста
Функция ВыполнитьОперациюЧерезCOM(АдресХранилища, ДанныеДляПередачи)
// Проверяем наличие данных в хранилище
Если ПустаяСтрока(АдресХранилища) Тогда
Возврат Ложь;
КонецЕсли;
// Извлекаем контейнер и получаем из него COM-соединение
Контейнер = ПолучитьИзВременногоХранилища(АдресХранилища);
Соединение = Контейнер[0];
// Теперь мы можем работать с внешней базой напрямую на сервере
Попытка
ВнешнийСправочник = Соединение.Справочники.Номенклатура;
// Выполняем необходимые действия...
Возврат Истина;
Исключение
ЗаписьЖурналаРегистрации("COM-Соединение", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
Возврат Ложь;
КонецПопытки;
КонецФункции
Важное предупреждение: Получать COM-объект из такого хранилища можно строго на той же стороне, где он был создан (в данном случае — на сервере приложения 1С). При попытке извлечь его на стороне клиента произойдет критическая ошибка передачи контекста. Кроме того, помните, что время жизни временного хранилища ограничено временем жизни формы, если при помещении использовался Уникальный Идентификатор этой формы.
Если вам требуется доступ к COM-соединению из глобального серверного контекста (например, из подписок на события документов), временное хранилище формы не подойдет, так как подписка ничего не знает о конкретной форме. В этом случае идеальным инструментом выступает общий модуль с установленным свойством «Повторное использование возвращаемых значений» (параметр «На время сеанса»).
Разберем по шагам, как настроить этот механизм:
КэшСоединенийПереопределяемый.Внутри модуля напишем функцию инициализации соединения. Платформа будет хранить результат выполнения этой функции в кэше сеанса, избавляя нас от необходимости повторного подключения при каждом проведении документа.
Функция ПолучитьАктивноеCOMСоединение(СтрокаПодключения) Экспорт
Соединитель = Новый COMОбъект("V83.COMConnector");
Попытка
Соединение = Соединитель.Connect(СтрокаПодключения);
Возврат Соединение;
Исключение
ВызватьИсключение "Не удалось установить COM-соединение с внешней базой: " + ОписаниеОшибки();
КонецПопытки;
КонецФункции
При вызове этой функции из подписки на событие платформа сначала проверит кэш сеанса. Если функция с такими параметрами уже вызывалась, 1С мгновенно вернет готовый COM-объект без повторной авторизации во внешней базе.
Анализ рисков кэширования: Кэш общего модуля не вечен. Платформа 1С автоматически очищает повторно используемые значения при наступлении следующих событий:
rphost) или нехватка оперативной памяти на сервере.ОбновитьПовторноИспользуемыеЗначения().Поэтому вызывающий код обязательно должен быть готов к тому, что соединение может оказаться разорванным или закрытым на стороне принимающей базы. Оберните вызовы во внешнюю базу в блок Попытка ... Исключение. Если соединение «отвалилось», необходимо принудительно обновить кэш и инициировать подключение заново.
Давайте посмотрим на проблему шире. Использование COM-соединений в современных управляемых приложениях 1С 8.3 считается устаревшей практикой (legacy). COM жестко привязан к операционной системе Windows. Если в будущем ваша организация решит перевести сервер приложений 1С на операционную систему Linux (что сейчас крайне актуально в рамках импортозамещения), вся ваша интеграция на COM-объектах мгновенно перестанет работать.
Поэтому наиболее прогрессивным и надежным решением является перевод обмена на HTTP-сервисы или Web-сервисы (SOAP).
Преимущества перехода на HTTP-сервисы:
Рассмотрим, как выглядит отправка данных из базы-источника на HTTP-сервис базы-приемника. На стороне приемника публикуется HTTP-сервис с шаблоном пути. На стороне источника пишется следующий код:
&НаСервере
Процедура ОтправитьДанныеЧерезHTTP(СтруктураДанных)
// Сериализуем данные в формат JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, СтруктураДанных);
СтрокаJSON = ЗаписьJSON.Закрыть();
// Настраиваем соединение с удаленным сервером 1С
Сервер = "192.168.1.100";
Порт = 80;
Пользователь = "ExchangeUser";
Пароль = "StrongPassword";
HTTPСоединение = Новый HTTPСоединение(Сервер, Порт, Пользователь, Пароль);
// Формируем POST-запрос к опубликованному HTTP-сервису
HTTPЗапрос = Новый HTTPЗапрос("/BaseReceiver/hs/ExchangeService/v1/SendDocument");
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json");
Попытка
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Если HTTPОтвет.КодСостояния = 200 Тогда
// Успешно обработано
Иначе
ВызватьИсключение "Сервер вернул ошибку с кодом: " + HTTPОтвет.КодСостояния;
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации("HTTP_Обмен", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Такой подход полностью развязывает руки администраторам и обеспечивает бесперебойную работу обмена вне зависимости от обновления платформы или изменения инфраструктуры.
Попытка проводить COM-подключения или отправлять веб-запросы непосредственно внутри транзакции записи (события ПриЗаписи или ОбработкаПроведения) — это опасная архитектурная ошибка.
Если удаленная база временно недоступна, зависла или в сети возникли неполадки, транзакция записи в вашей текущей базе будет оставаться открытой в ожидании таймаута соединения (который по умолчанию может составлять от 30 секунд до нескольких минут). Это приведет к следующим критическим последствиям:
Правильный архитектурный паттерн для решения этой задачи — Асинхронный обмен с использованием очереди. Разберем его по шагам:
ОчередьОбменаДанными (измерения: ОбъектАнализа (тип ЛюбаяСсылка), ресурс: Состояние (тип ПеречислениеСсылка.СостоянияОбмена), ДатаРегистрации).ПриЗаписи или ОбработкаПроведения вместо непосредственного подключения к другой базе делайте быструю запись в этот регистр. Операция записи в локальный регистр занимает доли миллисекунды и не нагружает транзакцию.ОчередьОбменаДанными.При такой схеме пользователи работают с максимальной скоростью, не зависят от доступности внешних систем, а обмен происходит практически в реальном времени с минимальным лагом в несколько секунд.
В процессе перевода Бухгалтерии на редакцию 3.0 сервер приложений 1С часто мигрирует на 64-разрядную архитектуру (64-bit). Здесь разработчики регулярно сталкиваются со следующей проблемой: сервер 1С работает в 64-битном процессе, а внешняя компонента V83.COMConnector или стороннее приложение (например, 32-битный MS Excel) являются 32-битными. Прямой вызов из 64-битного процесса 1С в 32-битную DLL завершится ошибкой инициализации COM-объекта.
Чтобы подружить 64-битный сервер 1С с 32-битным COM-соединителем, необходимо настроить службу компонентов Windows через механизм DCOM:
Win + R, введите comexp.msc /32 (флаг /32 обязателен для настройки 32-битных компонентов на 64-битной Windows) и нажмите Enter.USR1CV8), и ввести его пароль.USR1CV8 полные права на запуск, активацию и доступ к данному компоненту.После проведения этих настроек операционная система Windows будет автоматически запускать 32-битный COM-коннектор в выделенном суррогатном процессе (dllhost.exe), транслируя запросы из 64-битного сервера приложений 1С.
Проанализировав все варианты, мы видим, что переход на управляемое приложение требует качественного пересмотра устаревших интеграционных механизмов. Настоятельно рекомендуем уходить от прямого использования синхронных COM-соединений в пользу веб-сервисов и асинхронной очереди обмена.