При разработке сложных обработок в 1С:Предприятие 8.3 часто возникает задача разделения логики на этапы. Например, по одной кнопке мы загружаем и рассчитываем данные, а по второй — записываем их в базу. Если расчеты сложные и используют запросы с временными таблицами, возникает естественное желание: не формировать эти таблицы заново при нажатии второй кнопки, а использовать уже готовые.
В этой статье мы подробно разберем, почему нельзя просто так передать МенеджерВременныхТаблиц между вызовами сервера, и рассмотрим проверенные архитектурные решения, позволяющие сохранить результаты работы запросов между действиями пользователя.
Давайте проанализируем ситуацию, описанную в задаче. У нас есть два действия:
Главная ошибка, которую часто совершают начинающие разработчики, заключается в попытке сохранить объект МенеджерВременныхТаблиц в переменную модуля формы или передать его как параметр. Это невозможно по техническим причинам.
Рассмотрим, как устроена архитектура платформы:
&НаСервере — это отдельный сеанс связи с сервером приложений.МенеджерВременныхТаблиц — это "ручка", которая держит контекст этих таблиц в рамках текущего соединения.Как только процедура на сервере завершается и управление возвращается на клиент, контекст уничтожается. Соединение с СУБД закрывается (или возвращается в пул), и СУБД автоматически удаляет все временные таблицы, созданные в этом соединении. Поэтому к моменту, когда пользователь нажмет вторую кнопку, ваших таблиц уже физически не существует, даже если бы вы смогли сохранить ссылку на менеджер.
Наиболее универсальным и "чистым" с точки зрения архитектуры способом является сохранение данных в ВременноеХранилище. Давайте разберем этот алгоритм по шагам.
Идея заключается в том, чтобы не хранить сами временные таблицы (что невозможно), а хранить их содержимое в виде ТаблицаЗначений в оперативной памяти сервера или на диске (во временном хранилище), и восстанавливать их при необходимости.
Шаг 1. Формирование данных (Кнопка 1)
В первой процедуре после выполнения всех расчетов мы должны выгрузить содержимое важных временных таблиц в объект ТаблицаЗначений и поместить его во временное хранилище. Важно привязать хранилище к уникальному идентификатору формы, чтобы данные жили, пока открыта форма.
&НаСервере
Процедура ОбработатьФайлНаСервере()
// Создаем менеджер и выполняем запросы
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
// ... здесь ваш код формирования ВТ, например "ВТ_Данные" ...
Запрос.Текст = "ВЫБРАТЬ * ИЗ ВТ_Данные";
// 1. Выгружаем результат запроса в Таблицу Значений
ТЗ_Результат = Запрос.Выполнить().Выгрузить();
// 2. Помещаем ТЗ во временное хранилище
// Важно: передаем УникальныйИдентификатор формы вторым параметром!
АдресВХ = ПоместитьВоВременноеХранилище(ТЗ_Результат, УникальныйИдентификатор);
// 3. Сохраняем адрес в реквизит формы (строка)
// Реквизит "АдресДанныхКэша" нужно создать на форме заранее
ЭтотОбъект.АдресДанныхКэша = АдресВХ;
КонецПроцедуры
Шаг 2. Восстановление данных (Кнопка 2)
При нажатии второй кнопки мы проверяем, есть ли сохраненный адрес, получаем из него таблицу значений и снова загружаем её в запрос. СУБД обрабатывает такие операции очень быстро.
&НаСервере
Процедура ЗаписатьДанныеНаСервере()
Если ПустаяСтрока(ЭтотОбъект.АдресДанныхКэша) Тогда
Возврат; // Нет сохраненных данных
КонецЕсли;
// 1. Получаем таблицу из хранилища
ТЗ_ИзКэша = ПолучитьИзВременногоХранилища(ЭтотОбъект.АдресДанныхКэша);
Если ТЗ_ИзКэша = Неопределено Тогда
// Данные могли "протухнуть" или сеанс перезапущен
ВызватьИсключение "Данные устарели, повторите расчет.";
КонецЕсли;
// 2. Создаем новый менеджер для этого вызова
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
// 3. Загружаем ТЗ обратно во временную таблицу
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Восстановленная ИЗ &ТЗ КАК ТЗ";
Запрос.УстановитьПараметр("ТЗ", ТЗ_ИзКэша);
Запрос.Выполнить();
// Теперь у нас есть ВТ_Восстановленная, работаем с ней как обычно
// ... код записи данных ...
КонецПроцедуры
В обсуждении часто возникает вопрос: не будет ли это работать медленно из-за того, что мы гоняем данные из ВТ в ТЗ и обратно? Давайте проанализируем.
Миф о двойном расходе: Некоторые считают, что хранить данные и в хранилище, и в БД накладно. Однако, как верно замечено экспертами, ВременноеХранилище оптимизировано для таких задач. Данные сериализуются и хранятся на сервере приложений.
Реальная скорость: Операция Выгрузить() и загрузка параметра &ТЗ в запрос происходит в оперативной памяти (если объемы не гигантские). Для объема данных, озвученного в задаче (20 таблиц по 25 строк, или даже тысячи строк), время сериализации и десериализации составит доли миллисекунд. Это несопоставимо быстрее, чем заново читать файл с диска, парсить его и выполнять сложные вычисления.
Поэтому опасаться "лишних движений" данных в данном случае не стоит — это плата за отсутствие состояния (Stateless) между вызовами сервера, и эта плата минимальна.
Если структура ваших временных таблиц жесткая (вы точно знаете колонки и типы заранее) и объем данных невелик, можно использовать реквизиты формы.
ТаблицаЗначений (например, ДанныеДляЗаписи) с нужными колонками.ДанныеДляЗаписи.Загрузить(ТЗ_Результат).Преимущества: Не нужно возиться с адресами временного хранилища. Данные всегда под рукой.
Недостатки: Если строк станет много (десятки тысяч), форма начнет "тормозить" при каждом клике, так как эти данные будут передаваться на клиент и обратно при каждом серверном вызове. Временное хранилище лишено этого недостатка, так как на клиент передается только адрес (строка).
Подведем итоги обсуждения и выберем оптимальную стратегию.
Почему нельзя передать менеджер?
Потому что временные таблицы живут только в рамках одного соединения с СУБД, а между вызовами 1С соединение разрывается.
Что делать?
Использовать паттерн "Кэширование результата":
ТаблицаЗначений.ВременноеХранилище, привязав к UUID формы.Это решение является стандартом для разработки на платформе 1С:Предприятие 8 и обеспечивает лучший баланс между надежностью и производительностью.