Как заменить ПоместитьВоВременноеХранилище при переносе кода на платформу 1С 8.1

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

При переносе внешних отчетов, обработок или фрагментов конфигураций из современных версий платформы (1С 8.2 и 8.3) на старую платформу 1С 8.1 (например, при работе с комплексными конфигурациями вроде УПП 1.2/1.3 или КА 1.1) разработчики неизбежно сталкиваются с синтаксическими ошибками. Одной из самых частых проблем является отсутствие в платформе 1С 8.1 поддержки механизмов работы с временным хранилищем, а именно функций ПоместитьВоВременноеХранилище и ПолучитьИзВременногоХранилища.

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

Почему в 1С 8.1 нет временного хранилища и зачем оно нужно в 8.2+?

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

Временное хранилище (ВХ) в управляемом приложении выступает в роли «буфера» или моста. Когда клиенту нужно передать тяжелый объект на сервер (или между формами), платформа сериализует этот объект, помещает его во временную оперативную память сервера, а клиенту возвращает короткую строку — уникальный адрес (например, "e1cib/tempstorage/..."). Клиент оперирует только этим легким адресом, передавая его обратно на сервер для последующего извлечения объекта.

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

Анализ неверных решений: почему СохранитьЗначение() не работает?

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

  1. Проблема с синтаксисом. Метод СохранитьЗначение является процедурой, а не функцией. Попытка написать конструкцию вида Адрес = СохранитьЗначение(...) всегда будет вызывать системную ошибку компиляции: "Обращение к процедуре как к функции".
  2. Проблема с производительностью и объемом БД. Методы СохранитьЗначение и ВосстановитьЗначение предназначены исключительно для сохранения пользовательских настроек (например, запомнить последнюю выбранную организацию или склад при закрытии формы) между сеансами работы. Эти данные записываются на физический диск (в системные таблицы СУБД или в файлы профиля пользователя на локальном компьютере). Если вы попытаетесь сохранять туда оперативные данные (например, промежуточные таблицы значений на 10 000 строк), база данных начнет стремительно увеличиваться в объеме, а производительность системы упадет из-за постоянных дисковых транзакций.

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

Теперь рассмотрим рабочие и корректные варианты решения задачи.

Решение 1. Прямая передача объектов (Рекомендуемый способ)

Поскольку в обычных формах 1С 8.1 нет жестких ограничений на типы данных клиента, самый простой и производительный способ — отказаться от использования адресов и передавать сами объекты напрямую.

Выясним, как выглядит исходный код на платформе 8.2/8.3:


// Код в управляемой форме (8.2 / 8.3)
АдресХранилища = ПоместитьВоВременноеХранилище(ДеревоДанныхРаздела, УникальныйИдентификатор);
ПараметрыОткрытия = Новый Структура("АдресДанных", АдресХранилища);
ОткрытьФорму("ВнешнийОтчет.МойОтчет.Форма.ФормаПросмотра", ПараметрыОткрытия);

Проанализируем ситуацию и перепишем этот фрагмент под обычные формы 1С 8.1 без использования временного хранилища. Для этого мы передадим сам объект ДеревоЗначений через экспортную переменную или напрямую в свойства открываемой формы:


// Код в обычной форме (1С 8.1)
ФормаПриемник = ПолучитьФорму("ВнешнийОтчет.МойОтчет.Форма.ФормаПросмотра");

// Передаем сам объект напрямую в реквизит или переменную формы-приемника
ФормаПриемник.ДеревоДанныхРаздела = ДеревоДанныхРаздела; 
ФормаПриемник.Открыть();

В самой форме-приемнике (в ее модуле) в самом верху должна быть объявлена соответствующая экспортная переменная:

Перем ДеревоДанныхРаздела Экспорт;

Этот способ работает быстрее всего, так как передача объекта происходит по ссылке в оперативной памяти без сериализации и десериализации данных.

Решение 2. Сериализация в память (ЗначениеВСтрокуВнутр)

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

Системные функции ЗначениеВСтрокуВнутр и ЗначениеИзСтрокиВнутр позволяют мгновенно преобразовать любой сериализуемый объект (таблицу значений, структуру, массив, дерево значений) в системную XML-подобную строку и обратно. Эта строка хранится в оперативной памяти компьютера.

Рассмотрим пример замены кода. Допустим, в исходном коде управляемой формы было написано:


СтруктураРеквизитовФормы["АдресВоВрХранилище" + НаимРаздела] = ПоместитьВоВременноеХранилище(ДеревоДанныхРаздела);

Для платформы 1С 8.1 мы можем заменить эту строку следующим образом:


// Помещение объекта в текстовый "адрес" в памяти
СтруктураРеквизитовФормы["АдресВоВрХранилище" + НаимРаздела] = ЗначениеВСтрокуВнутр(ДеревоДанныхРаздела);

Соответственно, там, где в исходном коде выполнялось чтение из временного хранилища:


ДеревоДанных = ПолучитьИзВременногоХранилища(СтруктураРеквизитовФормы["АдресВоВрХранилище" + НаимРаздела]);

Мы производим обратное преобразование из строки:


// Восстановление объекта из текстовой строки
ДеревоДанных = ЗначениеИзСтрокиВнутр(СтруктураРеквизитовФормы["АдресВоВрХранилище" + НаимРаздела]);

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

Решение 3. Использование объекта ХранилищеЗначения

Если данные занимают значительный объем оперативной памяти, целесообразно использовать объект ХранилищеЗначения. В отличие от временного хранилища управляемых форм, этот объект существовал в 1С всегда (включая платформы 8.0 и 8.1). При создании объекта ХранилищеЗначения данные внутри него автоматически сжимаются по алгоритму DEFLATE (zip), что экономит оперативную память при передаче больших массивов информации.

Посмотрим на пример реализации:


// Упаковываем данные в сжатый контейнер
АрхивДанных = Новый ХранилищеЗначения(ДеревоДанныхРаздела);
СтруктураРеквизитовФормы["СжатыеДанные" + НаимРаздела] = АрхивДанных;

Для извлечения данных из контейнера в месте приема мы используем метод Получить():


// Извлекаем данные из контейнера
Если ТипЗнч(СтруктураРеквизитовФормы["СжатыеДанные" + НаимРаздела]) = Тип("ХранилищеЗначения") Тогда
    ДеревоДанныхРаздела = СтруктураРеквизитовФормы["СжатыеДанные" + НаимРаздела].Получить();
КонецЕсли;

Этот способ сочетает в себе безопасность хранения и экономию памяти без обращения к жесткому диску.

Решение 4. Использование временных файлов (ЗначениеВФайл)

Этот способ пригоден для случаев, когда передаваемые данные критически велики, или их необходимо гарантированно изолировать во внешнем источнике. Мы можем сериализовать объект во временный файл на диске, передать путь к этому файлу в качестве «адреса», а затем прочитать данные и обязательно удалить за собой временный файл.

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


// Шаг 1. Получаем уникальный путь к временному файлу в операционной системе
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("tmp");

// Шаг 2. Записываем объект в файл на диске
Попытка
    ЗначениеВФайл(ИмяВременногоФайла, ДеревоДанныхРаздела);
    СтруктураРеквизитовФормы["АдресФайлаДанных"] = ИмяВременногоФайла;
Исключение
    Сообщить("Не удалось записать временный файл: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;

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


ИмяФайла = СтруктураРеквизитовФормы["АдресФайлаДанных"];
ДеревоДанныхРаздела = Неопределено;

Если Не ПустаяСтрока(ИмяФайла) Тогда
    ФайлНаДиске = Новый Файл(ИмяФайла);
    Если ФайлНаДиске.Существует() Тогда
        // Читаем данные из файла
        ДеревоДанныхРаздела = ЗначениеИзФайла(ИмяФайла);
        
        // Обязательно удаляем временный файл после использования!
        Попытка
            УдалитьФайлы(ИмяФайла);
        Исключение
            // Файл мог быть заблокирован системой, обработаем исключение без аварийного завершения
            ЗаписьЖурналаРегистрации("ОчисткаВременныхФайлов", УровеньЖурналаРегистрации.Предупреждение,,, "Не удалось удалить файл: " + ИмяФайла);
        КонецПопытки;
    КонецЕсли;
КонецЕсли;

Внимание: Использование дисковых операций (чтение и запись файлов) работает существенно медленнее, чем передача данных напрямую в оперативной памяти (Решение 1 и Решение 2). Рекомендуется использовать этот метод только в крайних случаях.

Резюме

При обратном переносе кода с платформ 1С 8.2/8.3 на 1С 8.1 не пытайтесь построчно копировать вызовы временного хранилища. Оцените архитектуру решения: в 95% случаев в обычных формах достаточно напрямую присвоить передаваемый объект переменной или реквизиту формы. В остальных 5% случаев (когда требуется хранить строковый идентификатор) используйте связку функций ЗначениеВСтрокуВнутр и ЗначениеИзСтрокиВнутр.

← На главную