Разработка в среде управляемого интерфейса 1С:Предприятие 8 часто преподносит сюрпризы, связанные с передачей данных между клиентом и сервером. Типичный сценарий: на сервере заполняется сложная структура, содержащая вложенные массивы и соответствия, которая записывается в реквизит формы с произвольным типом данных. Однако при возврате на клиент разработчик обнаруживает, что часть данных (особенно находящихся внутри соответствий) бесследно исчезла или затерлась.
В этой статье мы подробно разберем причины такого поведения платформы 1С, проанализируем ограничения сериализации реквизитов формы и рассмотрим несколько надежных практических способов передачи сложных коллекций без потери данных. Для контроля итогового результата и поиска расхождений после программных доработок автоматически сверяют бухгалтерские и налоговые проводки между базами 1С.
Для понимания сути проблемы проанализируем, как управляемая форма обменивается данными с сервером. Этот процесс называется «круглым путем» (round-trip). Когда выполняется серверный вызов, весь контекст формы сериализуется (преобразуется в специальный внутренний формат передачи данных), отправляется на сервер, там обрабатывается, сериализуется обратно и возвращается на клиент.
Если реквизит формы имеет тип Произвольный, платформа пытается упаковать и передать все находящиеся в нем объекты. И здесь мы сталкиваемся со следующими жесткими ограничениями:
Соответствие принципиально не может быть сохранен в реквизитах формы. Платформа 1С не умеет сериализовать динамические соответствия для контекста формы ни напрямую, ни во вложенном виде (например, внутри структуры, которая лежит в реквизите формы). При передаче на клиент такие узлы просто игнорируются или очищаются.Произвольный оптимизируются платформой в целях экономии трафика. Если платформа «считает», что тип или его содержимое не поддерживаются на клиенте, данные будут молча отброшены во время обратной десериализации.ДанныеФормыКоллекция, ДанныеФормыСтруктура). Обычное Соответствие не имеет аналога в семействе «Данных формы», в отличие от массивов или структур.Рассмотрим подробнее четыре надежных и эффективных способа решения этой проблемы, которые позволят вам гарантированно сохранить структуру данных при передаче. При отладке и тестировании исправлений на большом количестве информационных баз автоматизируют их обновление и обслуживание в тихом режиме.
Это классический и самый надежный способ передачи абсолютно любых объектов 1С, которые поддерживают сериализацию (включая сложные вложенные соответствия, таблицы значений и т.д.). Суть метода заключается в том, что мы не храним сами данные в реквизите формы. Вместо этого мы помещаем объект во временное хранилище сеанса на сервере, а в реквизите формы (обычная строка) сохраняем только уникальный адрес этого хранилища.
Разберем по шагам, как реализовать этот механизм.
Шаг 1. На сервере подготавливаем данные, помещаем их во временное хранилище и записываем адрес в строковый реквизит формы (например, АдресПараметровВоВременномХранилище):
&НаСервере
Процедура ПодготовитьИСохранитьДанныеНаСервере()
// Создаем сложную структуру данных
СтруктураПараметров = Новый Структура;
СтруктураПараметров.Вставить("ОписаниеЗагрузки", "Импорт номенклатуры");
ОшибкиСопоставления = Новый Соответствие;
ОшибкиСопоставления.Вставить("Строка_15", "Не найден артикул");
ОшибкиСопоставления.Вставить("Строка_42", "Неверная единица измерения");
СтруктураПараметров.Вставить("ОшибкиСопоставления", ОшибкиСопоставления);
СтруктураПараметров.Вставить("ОбщиеОшибки", Новый Массив);
// Помещаем во временное хранилище, привязывая к УникальномуИдентификатору формы
АдресДанных = ПоместитьВоВременноеХранилище(СтруктураПараметров, ЭтаФорма.УникальныйИдентификатор);
// Записываем адрес в реквизит формы
Объект.АдресПараметровВоВременномХранилище = АдресДанных;
КонецПроцедуры
Шаг 2. На клиенте или при повторном обращении к серверу мы можем легко извлечь эти данные по сохраненному адресу:
&НаСервере
ПроцедураОбработатьДанныеИзХранилища()
// Проверяем наличие адреса
Если ЗначениеЗаполнено(Объект.АдресПараметровВоВременномХранилище) Тогда
// Получаем объект в первозданном виде со всеми соответствиями
СтруктураПараметров = ПолучитьИзВременногоХранилища(Объект.АдресПараметровВоВременномХранилище);
// Выполняем дальнейшую обработку
Ошибки = СтруктураПараметров.ОшибкиСопоставления;
// ...
КонецЕсли;
КонецПроцедуры
Важное преимущество: Данные во временном хранилище, привязанные к уникальному идентификатору формы, автоматически удаляются платформой при закрытии формы. Это исключает утечки памяти сеанса.
Если вам необходимо передавать соответствие с сервера на клиент для чтения, и при этом вы хотите хранить его именно внутри реквизита формы, стандартное Соответствие использовать нельзя. Однако платформа 1С предоставляет специализированный тип — ФиксированноеСоответствие.
Этот тип данных официально поддерживается платформой для хранения в реквизитах управляемой формы (как напрямую, так и внутри вложенных структур). Единственное его ограничение — он является неизменяемым («только для чтения») после создания.
Проанализируем пример конвертации динамического соответствия в фиксированное перед записью в реквизит формы на сервере:
&НаСервере
Процедура ЗаполнитьРеквизитФиксированнымСоответствием()
// Создаем обычную структуру параметров загрузки
СтруктураПараметров = Новый Структура;
// Создаем и заполняем стандартное динамическое соответствие
ДинамическоеСоответствие = Новый Соответствие;
ДинамическоеСоответствие.Вставить("Ключ1", "Значение1");
ДинамическоеСоответствие.Вставить("Ключ2", "Значение2");
// Преобразуем в ФиксированноеСоответствие при записи в структуру
СтруктураПараметров.Вставить("ОшибкиСопоставления", Новый ФиксированноеСоответствие(ДинамическоеСоответствие));
// Теперь эту структуру можно безопасно записать в произвольный реквизит формы
ЭтаФорма.СтруктураПараметровРеквизит = СтруктураПараметров;
КонецПроцедуры
После выполнения этой процедуры на сервере, на клиенте мы сможем без проблем прочитать данные из реквизита формы СтруктураПараметровРеквизит.ОшибкиСопоставления без потери ключей и значений.
Давайте выясним принципиальное отличие типа Соответствие от типа Структура. В соответствии ключом может выступать значение любого типа (например, число, дата или ссылка на справочник). В структуре ключом может быть только строка, соответствующая правилам идентификатора 1С. Для детального ознакомления с различиями в поведении этих коллекций можно воспользоваться сравнительным анализом использования структуры и соответствия.
Очень часто разработчики используют Соответствие по привычке, хотя ключами в нем выступают обычные строки. Если это ваш случай, настоятельно рекомендуется полностью заменить тип Соответствие на тип Структура.
Вложенные структуры и массивы внутри реквизитов формы передаются платформой между клиентом и сервером гораздо стабильнее и не подвержены риску «тихого» исчезновения данных при десериализации. Если же вам обязательно нужно использовать в качестве ключа ссылку или число, вы можете преобразовать их в строковое представление (например, использовать уникальный идентификатор ссылки или строковое представление числа с префиксом) и записывать данные в структуру.
Если структура данных очень сложная, динамическая и постоянно меняется как на клиенте, так и на сервере, отличным решением станет использование сериализации в формат JSON. Этот метод позволяет полностью отказаться от хранения объектов 1С в произвольных реквизитах формы, заменив их хранением обычной строки JSON.
Сериализация на уровне платформы работает чрезвычайно быстро. Рассмотрим, как это реализовать.
Код упаковки структуры на сервере:
&НаСервере
Функция ЗапаковатьДанныеВJSON(СтруктураДанных)
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
// Записываем структуру в строку JSON
ЗаписатьJSON(ЗаписьJSON, СтруктураДанных);
Возврат ЗаписьJSON.Закрыть();
КонецФункции
Код распаковки структуры на клиенте:
&НаКлиенте
Функция РаспаковатьДанныеИзJSON(СтрокаJSON)
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
// Читаем структуру обратно в объект 1С
Возврат ПрочитатьJSON(ЧтениеJSON);
КонецФункции
Строка JSON гарантированно и без искажений передается в любом реквизите формы с типом Строка неограниченной длины. На клиенте вы в любой момент можете быстро «распаковать» ее в обычную структуру 1С и работать с ней.
Часто программисты используют реквизиты формы как «склад» для временных данных алгоритма. Попробуем проанализировать ситуацию: действительно ли эти данные должны постоянно жить в форме и передаваться туда-обратно при каждом серверном вызове?
Если эти данные нужны только для выполнения конкретного процесса, их лучше вообще не хранить в реквизитах формы. Передавайте их напрямую как возвращаемые значения функций.
Посмотрим на пример правильной организации вызовов:
&НаКлиенте
Процедура ВыполнитьОбработкуДанных()
// Получаем структуру данных напрямую из функции, минуя реквизиты формы
ПараметрыЗагрузки = ПолучитьПараметрыНаСервере();
// Модифицируем или анализируем данные на клиенте
Если ПараметрыЗагрузки.ОбщиеОшибки.Количество() > 0 Тогда
Предупреждение("Обнаружены ошибки при подготовке данных!");
КонецЕсли;
// Передаем измененные данные обратно на сервер без привязки к контексту формы
ЗаписатьДанныеНаСервере(ПараметрыЗагрузки);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьПараметрыНаСервере()
Параметры = Новый Структура;
Параметры.Вставить("ОбщиеОшибки", Новый Массив);
// Заполнение параметров...
Возврат Параметры;
КонецФункции
&НаСервереБезКонтекста
Процедура ЗаписатьДанныеНаСервере(Знач ПараметрыЗагрузки)
// Обработка и сохранение данных на сервере
// Использование ключевого слова "Знач" предотвращает лишнее копирование данных на клиент
КонецПроцедуры
Использование директивы &НаСервереБезКонтекста существенно ускоряет работу приложения, так как при вызове процедур не передаются данные самой формы, а передаются только те параметры, которые мы явно указали в вызове функции. Для оценки производительности различных типов обращений можно воспользоваться исследованием эффективности контекстных и неконтекстных вызовов.
Подводя итоги, дадим несколько практических советов для выбора оптимального решения:
Произвольный (Решение 5).