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