Почему при вызове сервера в толстом клиенте обычного приложения возникает ошибка передачи мутабельного значения, а в управляемом режиме — нет?

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

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

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

Суть проблемы: ошибка передачи данных

Рассмотрим сценарий, описанный в обсуждении. У нас есть конфигурация, работающая в клиент-серверном варианте. Мы выполняем вызов серверной процедуры из модуля толстого клиента.

Пример кода, вызывающего ошибку в обычном приложении:


// Код на клиенте
Результат = СерверныйМодуль.ВыполнитьДействиеНаСервере();

// Код в общем модуле (Сервер)
Функция ВыполнитьДействиеНаСервере() Экспорт
    Попытка
        // Какое-то действие
        ВызватьИсключение "Тестовая ошибка";
    Исключение
        // Возвращаем объект ИнформацияОбОшибке
        Возврат ИнформацияОбОшибке();
    КонецПопытки;
КонецФункции

Если запустить этот код в Толстом клиенте (Управляемое приложение), он отработает корректно. Если же запустить тот же самый код в Толстом клиенте (Обычное приложение), мы получим ошибку: "Попытка передачи мутабельного значения".

Давайте разберемся, почему так происходит, ведь в обоих случаях используется "Толстый клиент".

Различия архитектуры: Обычное vs Управляемое приложение

Хотя тип клиента называется одинаково ("Толстый"), среда выполнения (контекст), в которой работает код, кардинально отличается.

  1. Толстый клиент в Управляемом режиме работает по архитектурным правилам управляемого приложения (нововведения платформы 8.2+). Механизмы обмена данными (маршалинга) между клиентом и сервером здесь унифицированы с тонким клиентом. Платформа использует оптимизированные механизмы XDTO-сериализации, которые умеют "упаковывать" и передавать некоторые сложные объекты, либо (в случае толстого клиента) могут использовать оптимизации управления памятью процесса.
  2. Толстый клиент в Обычном приложении — это legacy-режим (наследие версий 8.0/8.1). Когда из этого режима мы делаем вызов сервера (который появился как штатный механизм позже), платформа вынуждена строить "мост" между старой stateful-архитектурой (где состояние объектов хранится постоянно) и новой stateless-архитектурой сервера.

В этом "шлюзе" правила валидации данных работают строже. Платформа в обычном приложении не может гарантировать целостность мутабельных (изменяемых) объектов при их передаче через границу клиент-сервер.

Что такое мутабельные значения и почему они запрещены?

Мутабельные значения — это объекты, которые могут изменять свое состояние после создания. К ним относятся:

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

Тип ИнформацияОбОшибке особенно сложен: он может содержать внутри себя контекст возникновения ошибки, ссылки на модули и формы. В режиме Обычного приложения этот объект может захватить ссылки на контекст старого интерфейса (Windows-окна), которые физически невозможно сериализовать для передачи между процессами.

Решение 1: Настройка свойств Общих модулей

Если проблема возникает при использовании стандартных библиотек (например, БСП) или общих модулей, которые не обязательно должны выполняться на сервере при работе в толстом клиенте, мы можем изменить место исполнения кода.

В обсуждении было найдено эффективное решение для интеграции БСП в старую базу:

Установите флажок "Клиент (обычное приложение)" или "Толстый клиент" в свойствах общего модуля.

Если модуль скомпилирован и на клиенте, и на сервере, то при вызове метода из толстого клиента платформа предпочтет выполнить код локально (без вызова сервера), если это возможно. Это полностью исключает необходимость сериализации данных через сеть/процессы.

Решение 2: Использование Временного хранилища (Best Practice)

Если серверный вызов необходим (например, для доступа к данным, недоступным на клиенте), то единственным архитектурно верным способом передачи мутабельных данных является использование Временного хранилища.

Давайте перепишем наш пример правильно, чтобы он работал в любом режиме запуска:


// Код на клиенте
АдресВХранилище = СерверныйМодуль.ВыполнитьДействиеНаСервере();
Если ЗначениеЗаполнено(АдресВХранилище) Тогда
    // Получаем данные уже на клиенте
    ДанныеОшибки = ПолучитьИзВременногоХранилища(АдресВХранилище);
    // Обрабатываем ошибку...
КонецЕсли;

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

Метод ПоместитьВоВременноеХранилище специально разработан для сохранения любых сериализуемых данных на сервере и возврата клиенту строкового адреса (UUID), который является простым типом (строка) и легко передается.

Решение 3: Упрощение возвращаемых данных

Если вам не нужен весь сложный объект ИнформацияОбОшибке, а достаточно только текста, не возвращайте сам объект. Верните структуру или строку.

Правильный подход с сериализацией в простые типы:


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

Аналогично следует поступать со СпискомЗначений. В Синтаксис-помощнике для СписокЗначений есть примечание о доступности и сериализации, но для обычного приложения надежнее преобразовать список в Массив структур перед возвратом на клиент.

Выводы

  1. Толстый клиент в режиме обычного приложения и в режиме управляемого приложения — это разные среды исполнения с разными правилами маршалинга данных.
  2. Прямая передача объектов типа ИнформацияОбОшибке, СписокЗначений с сервера на клиент в обычном приложении не поддерживается и вызывает ошибку мутабельности.
  3. Для решения проблемы используйте Временное хранилище или преобразуйте данные в простые типы (Структуры, Массивы, Строки).
  4. В ряде случаев помогает включение флагов компиляции модуля на клиенте, чтобы избежать серверного вызова.
← На главную