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