Как исправить ошибку мутабельности при передаче объекта из подписки на события на сервер в 1С

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

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

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

Для начала разберем природу возникновения проблемы. В 1С существуют понятия «сериализуемых» и «мутабельных» (изменяемых) типов данных. Мутабельные типы, такие как СправочникОбъект, ДокументОбъект или ТаблицаЗначений, не могут быть переданы между контекстами клиента и сервера «как есть», так как они представляют собой сложные ссылки на области памяти, которые актуальны только там, где объект был создан.

В обычном приложении (Толстый клиент), если пользователь открыл форму и нажал кнопку «Записать», создание и инициализация объекта происходят непосредственно в клиентском сеансе. Следовательно, и обработчики событий, включая подписки на события, вызываются в контексте клиента. Когда код внутри подписки пытается вызвать процедуру общего модуля, у которого стоит флаг Сервер, платформа пытается отправить Источник (наш объект) на сервер. Поскольку объект мутабелен, попытка сериализации заканчивается ошибкой.

Решение 1: Передача данных через сериализуемые коллекции

Рассмотрим наиболее правильный с точки зрения архитектуры способ. Вместо того чтобы передавать на сервер весь тяжелый объект, мы можем извлечь из него только те данные, которые необходимы для выполнения логики в привилегированном режиме. Для этого используются типы Структура, Массив или Соответствие.

Проанализируем пример кода в подписке на стороне клиента:


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

В этом случае на сервере мы можем получить объект заново по ссылке через Источник.Ссылка.ПолучитьОбъект(), если нам нужно внести в него изменения, или просто использовать данные из структуры для проведения проверок.

Решение 2: Использование свойства ДополнительныеСвойства

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

Посмотрим, как это работает:

  1. В подписке на клиенте мы заполняем Источник.ДополнительныеСвойства.
  2. В модуле объекта (в процедурах ПередЗаписью или ПриЗаписи), который уже может выполняться на сервере, мы считываем эти данные.

Важно: Помните, что ДополнительныеСвойства также должны содержать только сериализуемые значения, если предполагается переход контекста.

Решение 3: Правильная настройка флагов общих модулей

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

Чтобы избежать ошибки мутабельности без передачи параметров, можно попробовать перенести логику в модуль, где доступен серверный контекст, но вызвать его правильно. Однако следует помнить правило: где живет объект, там и выполняется подписка. Если объект на клиенте — подписка на клиенте. Исправить «мозги» платформе и заставить её перебросить мутабельный объект на сервер автоматически невозможно.

Решение 4: Привилегированный режим и контекст исполнения

Часто разработчики пытаются использовать УстановитьПривилегированныйРежим(Истина) прямо в клиентской подписке. Выясним, почему это не работает. Метод УстановитьПривилегированныйРежим имеет доступность: Сервер, толстый клиент (только в файловом варианте), внешнее соединение. В клиент-серверном варианте вызов этого метода на стороне клиента просто игнорируется платформой. Права не повышаются.

Для корректной работы с привилегированным режимом нам в любом случае необходимо попасть на сервер. Рассмотрим шаги:

  1. Подписка на клиенте собирает данные в Структуру.
  2. Вызывается серверная функция общего модуля (с установленным флагом Вызов сервера).
  3. Внутри этой функции вызывается УстановитьПривилегированныйРежим(Истина).
  4. Выполняются действия от имени администратора.

Решение 5: XML-сериализация как крайний метод

Если объект еще не записан в базу (новая ссылка), а данных в нем слишком много, чтобы вручную перекладывать их в структуру, можно прибегнуть к сериализации в XML. Это позволяет превратить весь объект в строку, передать её на сервер и там десериализовать обратно в объект.

Разберем примерную схему:


// На клиенте
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
ЗаписатьXML(ЗаписьXML, Источник);
СтрокаXML = ЗаписьXML.Закрыть();

// Передаем СтрокаXML на сервер
МойСерверныйМодуль.ОбработатьОбъектИзXML(СтрокаXML);

На сервере мы выполняем обратное действие через ЧтениеXML и ПрочитатьXML. Это «честный» способ передать текущее состояние объекта, включая все табличные части, без возникновения ошибки мутабельности.

Резюме по стратегии обхода

Подводя итог, при работе с подписками в обычном приложении мы рекомендуем придерживаться следующей последовательности действий:

  1. Минимизируйте передачу объектов: Старайтесь передавать только ссылку (Источник.Ссылка) и вспомогательные параметры через Структуру.
  2. Контролируйте контекст: Помните, что флаг Вызов сервера необходим для перехода из клиентской подписки в серверный код.
  3. Используйте ДополнительныеСвойства: Для передачи флагов управления логикой записи это самый эффективный и быстрый инструмент.
  4. Проверяйте вариант работы базы: Поведение УстановитьПривилегированныйРежим в файловой и клиент-серверной базах на толстом клиенте различается, что может маскировать ошибки при разработке.

Таким образом, проблема мутабельности — это не «гадость» платформы, а архитектурное ограничение, призванное защитить целостность данных при передаче между физическими узлами системы (клиентским компьютером и сервером приложений).

← На главную