Почему пропадают данные из ХранилищаЗначения в табличной части управляемой формы?

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

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

Разбор исходной ситуации

Рассмотрим сценарий: у нас есть справочник с табличной частью, один из реквизитов которой имеет тип ХранилищеЗначения. На сервере мы пытаемся программно заполнить эту таблицу, сериализуя массив типов в хранилище. Типичный код, который может привести к ошибке, выглядит следующим образом:


// Фрагмент кода на сервере
ЭлементОбъект = ДанныеФормыВЗначение(Объект, Тип("СправочникОбъект.ИмяСправочника"));
ТЧШапка = ЭлементОбъект.Шапка;
ТЧШапка.Очистить();

Для каждого ЭлементКоллекции ИЗ КоллекцияРеквизитов Цикл
    нСтрока = ТЧШапка.Добавить();
    ЗаполнитьЗначенияСвойств(нСтрока, ЭлементКоллекции);
    
    // Попытка записать массив типов в ХранилищеЗначения
    ВремХранилище = Новый ХранилищеЗначения(ЭлементКоллекции.Тип.Типы(), Новый СжатиеДанных(9));
    нСтрока.Типы = ВремХранилище;
КонецЦикла;

// Возврат данных в форму
ЗначениеВДанныеФормы(ЭлементОбъект, Объект);

На первый взгляд, алгоритм верный: данные помещены в объект, а затем переданы обратно в структуру ДанныеФормы. Однако при следующем обращении к серверу или попытке извлечь данные из Объект.ТЧШапка[0].Типы.Получить() мы получаем пустое значение. Выясним причину этого поведения.

Причина 1: Специфика работы ДанныеФормы и клиент-серверного взаимодействия

Основная особенность заключается в том, как платформа 1С:Предприятие 8.3 обрабатывает тип ХранилищеЗначения в контексте управляемой формы. Объекты типа ХранилищеЗначения никогда не передаются на клиент.

Когда мы вызываем процедуру ЗначениеВДанныеФормы, платформа честно копирует данные из объекта справочника в структуру данных формы. Однако структура ДанныеФормы на клиенте не может содержать "тяжелый" бинарный объект. В результате, при отправке формы пользователю (на клиент), содержимое хранилища отсекается. Если после этого происходит любой другой вызов сервера, форма возвращается обратно на сервер уже "пустой" в части этого реквизита. Поскольку данные не были записаны в базу данных, а существовали только в оперативной памяти объекта, они безвозвратно теряются.

Причина 2: Сериализация типов и метаданных

Вторая проблема кроется в содержимом самого хранилища. Автор темы пытается сохранить ЭлементКоллекции.Тип.Типы(). Метод Типы() возвращает массив объектов типа Тип. Если эти типы относятся к внешним источникам данных, при их сериализации могут возникнуть проблемы.

Проанализируем ситуацию: массив типов — это набор ссылок на описания в метаданных. При помещении в ХранилищеЗначения происходит XML-сериализация. Если в процессе десериализации (при методе Получить()) платформа не может однозначно восстановить ссылку на объект метаданных (например, из-за специфики сеанса или контекста внешнего источника), она вернет Неопределено.

Решение 1: Использование временного хранилища

Для работы с данными в памяти формы, которые не должны потеряться между вызовами, лучше всего использовать Временное хранилище. Вместо того чтобы записывать данные непосредственно в реквизит типа ХранилищеЗначения табличной части, мы можем хранить там строковый адрес.

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

  1. Добавим в табличную часть формы скрытый реквизит АдресДанных (тип Строка).
  2. При заполнении таблицы на сервере поместим массив типов во временное хранилище, используя уникальный идентификатор формы.
  3. Сохраним полученный адрес в реквизит АдресДанных.
  4. Окончательную запись в реальное ХранилищеЗначения объекта базы данных выполним только в процедуре ПередЗаписьюНаСервере.

// Пример правильного заполнения через временное хранилище
Для каждого ЭлементКоллекции ИЗ КоллекцияРеквизитов Цикл
    нСтрока = Объект.Шапка.Добавить();
    МассивТипов = ЭлементКоллекции.Тип.Типы();
    // Помещаем во временное хранилище, привязывая к форме
    нСтрока.АдресДанных = ПоместитьВоВременноеХранилище(МассивТипов, ЭтаФорма.УникальныйИдентификатор);
КонецЦикла;

Решение 2: Сохранение имен типов вместо самих объектов Тип

Чтобы избежать проблем с сериализацией сложных объектов метаданных, рекомендуется хранить не сами типы, а их строковые представления. Это гораздо надежнее и проще для отладки. Проанализируем, как это реализовать:

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


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

Как показывают тесты, простые типы и массивы строк сериализуются и десериализуются платформой без ошибок, в отличие от объектов, имеющих внутренние связи с метаданными внешних источников.

Решение 3: Правильная фиксация данных в объекте

Если вам все же необходимо использовать именно реквизит объекта с типом ХранилищеЗначения, помните, что данные в нем "выживут" только в том случае, если объект будет записан в базу данных до того, как управление вернется на клиент. Рассмотрим стратегию работы через процедуру ПередЗаписьюНаСервере:

В этой процедуре мы получаем объект из данных формы, заполняем его реквизиты (включая хранилища значений) и позволяем платформе выполнить стандартную запись. В этом контексте данные не будут передаваться на клиент в промежуточном состоянии, а сразу попадут в СУБД.

Итоги и рекомендации

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

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

Следуя этим правилам, вы избежите потери данных и обеспечите корректную работу ваших механизмов интеграции с внешними источниками данных в 1С.

← На главную