Как исправить ошибку «Попытка передачи с клиента на сервер мутабельного значения» при записи документа через COM-соединение в 1С?

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

При интеграции различных информационных баз 1С между собой разработчики часто используют механизм COM-соединения. Этот способ позволяет напрямую обращаться к данным другой конфигурации, создавать, изменять и записывать объекты. Однако при работе в клиент-серверном варианте (особенно на СУБД SQL) программисты регулярно сталкиваются со специфическими ошибками платформы.

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

Часть 1. Корректное создание COM-соединения и первая типичная ошибка

Проанализируем исходный код, с которого начал автор темы на форуме:


ПараметрыПодключения = "Srvr=""имясервера"";Ref=""имябазы"";Usr=""логин"";Pwd=""пароль"";";
ИмяКомОбъекта = "";
СистемнаяИнформация = Новый СистемнаяИнформация;
Если СтрНайти(СистемнаяИнформация.ВерсияПриложения, "8.3") > 0 Тогда
     ИмяКомОбъекта = "V83.COMConnector";
ИначеЕсли СтрНайти(СистемнаяИнформация.ВерсияПриложения, "8.2") > 0 Тогда
     ИмяКомОбъекта = "V82.COMConnector";
Иначе
     ВызватьИсключение "Не удалось определить версию платформы. " + ОписаниеОшибки();
КонецЕсли;
 Соединение = Новый COMОбъект(ИмяКомОбъекта);
 НужныйДок = Соединение.Документы.РеализацияТоваровУслуг; 

При выполнении этого кода система выдает ошибку: Поле объекта не обнаружено (Документы). Выясним причину этого поведения.

Дело в том, что объект V83.COMConnector сам по себе является лишь «менеджером» или «клиентом» для организации связи. Он не хранит внутри себя структуру метаданных целевой базы. Чтобы начать работать с объектами (справочниками, документами, регистрами) удаленной информационной базы, необходимо установить фактическое соединение с помощью метода Connect(), передав в него строку подключения.

Посмотрим на исправленный и правильный вариант инициализации подключения:


// Инициализируем коннектор
COMConnector = Новый COMОбъект(ИмяКомОбъекта);

// Устанавливаем непосредственное соединение с базой данных
Соединение = COMConnector.Connect(ПараметрыПодключения);

// Теперь объект "Соединение" содержит контекст удаленной базы, 
// и мы можем обращаться к ее метаданным
НужныйДок = Соединение.Документы.РеализацияТоваровУслуг.СоздатьДокумент();

После вызова метода Connect() переменная Соединение возвращает указатель на контекст внешней базы данных. Теперь обращение к свойству Соединение.Документы отработает корректно.

Часть 2. Почему возникает ошибка «передачи мутабельного значения» при записи документа?

Двигаемся дальше по шагам. Соединение успешно установлено, документ создан и заполнен. Однако при попытке вызвать метод Док.Записать() в целевой базе транзакция обрывается со следующей ошибкой:

Ошибка при вызове метода контекста (Записать). Произошла исключительная ситуация. Ошибки при выполнении обработки - ПриЗаписи. Попытка передачи с клиента на сервер мутабельного значения 1-го параметра.

Разберем природу этого явления. Термин «мутабельный» (от англ. mutable) означает изменчивый, динамический тип данных. К мутабельным объектам в системе 1С:Предприятие относятся тяжелые типы данных, состояние которых может меняться в процессе работы: например, ДокументОбъект, СправочникОбъект, ТаблицаЗначений, ДеревоЗначений, СписокЗначений.

В клиент-серверной архитектуре передача таких объектов напрямую между клиентским приложением и сервером запрещена платформой из соображений оптимизации трафика и целостности данных. При работе через COM-соединение выполняются следующие правила:

  1. Запущенный сеанс COM-соединения (через V83.COMConnector) классифицируется платформой как Внешнее соединение.
  2. Для целевой базы данных это внешнее соединение выступает в роли «клиента».
  3. В момент вызова метода Записать() в удаленной базе начинают отрабатывать стандартные механизмы: процедуры модуля объекта (ПередЗаписью, ПриЗаписи), а также назначенные на эти события Подписки на события.
  4. Если в одной из таких подписок или процедур общего модуля происходит вызов серверного метода (например, общего модуля с установленным свойством «Сервер»), а в качестве параметра передается сам объект (переменная Источник или ЭтотОбъект), платформа пытается передать его из клиентского контекста внешнего соединения на сервер.
  5. В этот момент происходит аварийное прерывание с ошибкой передачи мутабельного значения.

Часть 3. Способы решения проблемы

Рассмотрим подробнее три основных способа обхода и устранения этой ошибки в зависимости от ваших задач и доступа к коду целевой базы.

Способ 1. Использование режима загрузки данных (без изменения кода целевой базы)

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

Для этого перед вызовом метода Записать() необходимо установить свойство объекта ОбменДанными.Загрузка в значение Истина. Проанализируем пример кода:


Док = Соединение.Документы.РеализацияТоваровУслуг.СоздатьДокумент();
Док.Номер = Объект.Документ.Номер;
Док.Дата = Объект.Документ.Дата;
Док.Организация = Соединение.Справочники.Организации.НайтиПоКоду("000000001"); 

// Включаем режим загрузки данных, чтобы отключить стандартные обработчики и подписки
Док.ОбменДанными.Загрузка = Истина;

Док.Записать();

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

Недостатки: Не выполняются типовые проверки конфигурации, не заполняются связанные регистры (если это было завязано на обработчики событий), документ записывается в «сыром» виде.

Способ 2. Корректировка свойств Общих модулей в целевой конфигурации

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

Выясним причину ошибки в конфигураторе целевой базы:

  1. Откроем журнал регистрации или воспользуемся отладчиком, подключившись к сеансу внешнего соединения, чтобы локализовать, на каком именно общем модуле спотыкается система.
  2. Найдем данный Общий модуль в дереве метаданных конфигуратора.
  3. Откроем палитру свойств этого общего модуля.
  4. Убедимся, что у модуля установлен флаг «Внешнее соединение» (External Connection). Если данный флаг снят, то код модуля не может быть выполнен в контексте нашей COM-сессии. Из-за этого платформа вынуждена передавать управление на сервер, пытаясь перетащить туда мутабельный объект, что и приводит к ошибке.
  5. При необходимости установите флаг «Вызов сервера» для модулей, методы которых должны вызываться из внешнего соединения.

Способ 3. Переписывание обработчиков событий (для разработчиков целевой базы)

Если ошибка возникает в вашей собственной подписке на событие или в кастомном общем модуле целевой конфигурации, правильным архитектурным решением будет рефакторинг этого кода. Проанализируем, как избежать передачи мутабельного объекта.

Плохой вариант (вызывает ошибку при COM-соединении):


// Код в подписке на событие "ПриЗаписи"
Процедура ПриЗаписиДокумента(Источник, Отказ)
    // Передаем весь объект "Источник" (мутабельное значение) на сервер
    РаботаСДокументамиСервер.ОбработатьДокумент(Источник); 
КонецПроцедуры

Хороший вариант (исправленный код):

Вместо передачи тяжелого объекта целиком, передавайте его уникальную ссылку (тип ДокументСсылка не является мутабельным и отлично передается между контекстами клиента, сервера и внешнего соединения):


Процедура ПриЗаписиДокумента(Источник, Отказ)
    // Передаем ссылку на документ, которая легко сериализуется
    РаботаСДокументамиСервер.ОбработатьДокументПоСсылке(Источник.Ссылка); 
КонецПроцедуры

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


Процедура ПередЗаписьюДокумента(Источник, Отказ)
    ПараметрыДокумента = Новый Структура;
    ПараметрыДокумента.Вставить("Дата", Источник.Дата);
    ПараметрыДокумента.Вставить("Организация", Источник.Организация);
    
    // Передаем легкую структуру вместо тяжелого объекта
    РаботаСДокументамиСервер.ПроверитьДанные(ПараметрыДокумента);
КонецПроцедуры

Резюме

Мы детально разобрали основные сложности, возникающие при создании и записи документов в 1С через COM-соединение. Помните два золотых правила:

← На главную