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