Как вызвать серверную процедуру из внешнего соединения (COM) и избежать ошибки передачи мутабельных значений?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции систем на базе 1С через механизм COM-соединения (External Connection) разработчики часто сталкиваются с труднопреодолимым препятствием: попыткой вызвать серверный код, который успешно работает в обычном сеансе, но выдает ошибку во внешнем. Одной из самых коварных проблем является ошибка «Попытка передачи мутабельного значения». Рассмотрим подробнее, почему это происходит и как элегантно обойти это ограничение, не снимая конфигурацию с поддержки.

Суть проблемы: конфликт контекстов и мутабельность

Когда мы подключаемся к базе 1С через COMConnector, создается специальный тип сеанса — Внешнее соединение. Несмотря на то, что в серверной базе этот сеанс физически выполняется на стороне сервера приложений, его контекст сильно ограничен. Проанализируем основные причины возникновения ошибок:

  1. Флаги общих модулей: Если процедура находится в модуле, где установлена только галка Сервер, но отсутствует галка Внешнее соединение, она будет недоступна для прямого вызова из COM.
  2. Мутабельные значения: К ним относятся объекты, которые могут изменяться — ТаблицаЗначений, ДеревоЗначений, Структура, Соответствие, а также ссылки на объекты, которые еще не записаны. Платформа 1С блокирует передачу таких объектов между «клиентом» (которым в данном случае выступает COM-поток) и сервером, если они не могут быть однозначно сериализованы.
  3. Вложенные вызовы: Даже если вы вызываете стандартный метод Записать(РежимЗаписиДокумента.Проведение), внутри типового кода могут вызываться процедуры из модулей, не предназначенных для внешнего соединения. Именно в этот момент возникает ошибка, так как контекст выполнения «тянет» за собой ограничения исходного вызова.

Метод 1. Смена контекста через расширение (Рекомендуемый)

Проанализируем ситуацию: нам нужно выполнить проведение документа в типовой конфигурации, которую нельзя менять. Прямой вызов Документ.Записать() из COM приводит к ошибке из-за сложной внутренней логики проведения. Самый эффективный способ — «перепрыгнуть» из контекста внешнего соединения в чистый серверный контекст. Сделать это можно с помощью расширения.

Разберем по шагам реализацию этого метода:

  1. Создадим новое расширение в базе данных.
  2. Добавим в него новый Общий модуль (например, ЭффективноеПроведение_ВнешнийВызов).
  3. В свойствах модуля обязательно установим две галочки: Сервер и Вызов сервера.
  4. Внутри модуля напишем процедуру-обертку, которая будет принимать ссылку на документ.

Посмотрим на пример кода в расширении:


Процедура ВыполнитьПроведениеНаСервере(СсылкаНаДокумент, РежимПроведения) Экспорт
    // Здесь мы уже находимся в полноценном серверном контексте
    // Платформа воспринимает этот вызов как полноценный серверный вызов
    ОбъектДокумента = СсылкаНаДокумент.ПолучитьОбъект();
    Если ОбъектДокумента <> Неопределено Тогда
        ОбъектДокумента.Записать(РежимЗаписиДокумента.Проведение, РежимПроведения);
    КонецЕсли;
КонецПроцедуры

Теперь из вашего внешнего приложения (или другой базы через COM) вы вызываете не метод Записать() самого объекта, а процедуру этого модуля:


// Пример на языке 1С для вызова через COM
БазаCOM = COMConnector.Connect(СтрокаСоединения);
БазаCOM.ЭффективноеПроведение_ВнешнийВызов.ВыполнитьПроведениеНаСервере(СсылкаНаДокумент, РежимПроведения);

Почему это работает? Благодаря галке Вызов сервера в расширении, обращение к этому модулю инициирует смену контекста. Платформа переходит из ограниченного сеанса COM в стандартный серверный вызов, где доступны все модули с галкой Сервер, и проблема мутабельных значений при передаче внутренних данных внутри проведения исчезает.

Метод 2. Использование фоновых заданий

Выясним причину, по которой этот метод может быть полезен. Фоновое задание всегда запускается в собственном серверном сеансе, который никак не связан с ограничениями COM-соединения. Если проведение документа крайне сложное и метод с расширением по каким-то причинам не подходит, можно запустить процедуру проведения асинхронно.

Рассмотрим алгоритм действий:

  1. В COM-соединении вызываем функцию ФоновыеЗадания.Выполнить().
  2. Передаем в нее имя экспортной процедуры серверного модуля, которая выполнит работу.
  3. В COM-соединении ожидаем завершения задания, анализируя состояние объекта ФоновоеЗадание.

Пример реализации запуска:


// В вызывающем коде через COM
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(СсылкаНаДокумент);
Задание = БазаCOM.ФоновыеЗадания.Выполнить("МойСерверныйМодуль.ПровестиДокумент", ПараметрыЗадания);

// Ожидание (опционально)
Задание.ОжидатьЗавершения(10); // Ждем 10 секунд

Метод 3. Переход на HTTP-сервисы

Проанализируем современный подход. Фирма 1С постепенно уходит от поддержки технологии COM в пользу веб-технологий. Использование HTTP-сервисов позволяет полностью избавиться от проблемы мутабельных значений, так как обмен данными происходит через простые типы (строки JSON или XML).

Разберем преимущества этого способа:

Важные рекомендации по работе с мутабельными значениями

Если вы все же вынуждены работать через COM и не можете использовать расширения, обратите внимание на следующие моменты:

Избегайте передачи таблиц значений: Если вам нужно передать данные извне для заполнения документа, преобразуйте ТаблицуЗначений в Массив структур. Массивы и структуры (содержащие только простые типы) лучше проходят сериализацию, хотя и с ними нужно быть осторожными.

Сериализация в строку: Иногда проще всего сериализовать сложный объект в XML или JSON на стороне отправителя и десериализовать его на стороне сервера 1С внутри общего модуля. Это гарантированный способ избежать ошибки мутабельности.

Используйте ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр() только если обе стороны — базы 1С одной версии, однако помните, что это «тяжелый» механизм. Лучшим выбором будет использование XDTO или JSON.

Резюме

Подводя итог, можно сказать: наиболее надежным и современным способом «пробить» барьер контекста внешнего соединения в типовой конфигурации является создание модуля-прослойки в расширении с включенным флагом Вызов сервера. Это позволяет сохранить конфигурацию на поддержке и обеспечить стабильное выполнение кода без переписывания сложных алгоритмов проведения.

← На главную