Имя: Пароль:
1C
 
Рассказываю, как работать с кэшем в формах. Дешево и сердито...
0 Гений 1С
 
гуру
16.03.08
21:59
Короче, запарился я рассчитывать вычисляемые поля для формы списка, да еще и так, чтобы это было быстро...

В общем написал функцию для кэширования. Изучайте други, все просто.


В форме, используется функция приоткрытии, показываю также образцы функций-диспетчеров:

Процедура ПриОткрытии()
...
   М=Новый Массив();
   М.Добавить("Состояние");
   М.Добавить("Категории");
   М.Добавить("Внимание");
   обСоздатьКэшФормы(ЭтаФорма, "КэшЭлементов", М);
...    
КонецПроцедуры

Функция ДиспетчерВычисленийКэша(П, ИмяКолонки, Ключи) Экспорт
   Если ИмяКолонки="Категории" Тогда
       ТЗ=пимПолучитьМассивКатегорийОбъектов(Новый Структура("Дата, Ссылки", ТекущаяДата(), Ключи));
       ТЗ.Колонки.Ссылка.Имя="Ключ";
       Возврат ТЗ;
   ИначеЕсли ИмяКолонки="Состояние" Тогда
       ТЗ=пимПолучитьСостоянияЗадач(Новый Структура("Дата, Ссылки", ТекущаяДата(), Ключи));
       ТЗ.Колонки.Ссылка.Имя="Ключ";
       Возврат ТЗ;
   КонецЕсли;
КонецФункции

Функция ДиспетчерВыводаКэша(П, ОформлениеСтроки, СтрокаКэша, ИмяКолонки=Неопределено) Экспорт
   Если ИмяКолонки="Категории" Тогда
       ОформлениеСтроки.Ячейки.Категории.УстановитьТекст(СтрокаКэша.Категории);
   ИначеЕсли ИмяКолонки="Состояние" Тогда
       Картинка=пимКартинкаСостоянияЗадачи(СтрокаКэша.Состояние);
       Если Картинка<>Неопределено Тогда
           ОформлениеСтроки.Ячейки.Состояние.УстановитьКартинку(Картинка);
       КонецЕсли;
   КонецЕсли;
КонецФункции


Процедура СправочникСписокПриПолученииДанных(Элемент, ОформленияСтрок)
   
   //Элементы=Новый Массив();
   //ЭлементыКатегории=Новый Массив();
   //ЭлементыАктуальность=Новый Массив();
   
   
   
   
   //Отрисовываем то, что в кэше...
   обОтрисоватьКэш(Новый Структура("Форма, ОформленияСтрок, Элемент, ДиспетчерВычислений, ДиспетчерВывода, КолонкаКлюча", ЭтаФорма, ОформленияСтрок,  ЭлементыФормы.СправочникСписок, "ДиспетчерВычисленийКэша", "ДиспетчерВыводаКэша", "Ссылка"));
КонецПроцедуры




Модуль с используемыми функциями:



//ПОДБИБЛИОТЕКА: КЭШ ФОРМЫ

//20071115 fixin
//Создает кэш в форме для кэширования данных табличной части
Функция обСоздатьКэшФормы(Форма, ИмяКэша, Колонки=Неопределено)Экспорт
   КэшЭлемент=Форма.ЭлементыФормы.Добавить(Тип("ПолеВвода"),"_c4dc2eda1dcc454b94b013f8fdb951b9"+ИмяКэша, ложь);
   ТЗ=Новый ТаблицаЗНачений();
   ТЗ.Колонки.Добавить("Ключ");
   Если Колонки<>Неопределено Тогда
       Для Каждого Колонка Из Колонки Цикл
           ТЗ.Колонки.Добавить(Колонка);
       КонецЦикла;
   КонецЕсли;
   Структура=Новый Структура();
   Структура.Вставить("Данные", ТЗ);
   Структура.Вставить("Колонки", обСкопироватьМассив(Колонки));
   КэшЭлемент.СписокВыбора.Очистить();
   КэшЭлемент.СписокВыбора.Добавить(Структура);
КонецФункции

//20071115 fixin
Функция обВернутьКэшФормы(Форма, ИмяКэша) Экспорт
   Возврат Форма.ЭлементыФормы["_c4dc2eda1dcc454b94b013f8fdb951b9"+ИмяКэша].СписокВыбора[0].Значение;
КонецФункции

//20071115 fixin
Функция обОчиститьКэш(Кэш) Экспорт
   Кэш.Данные.Очистить(); //Очищает таблицу значений
КонецФункции

//20071115 fixin
//Ищет значение в кэше, если Создавать=истина, то если не находит, добавляет
//Возврат - Неопределено или строку кэша.
Функция обНайтиВКэше(Кэш, Значение, Создавать=ложь) Экспорт
   Стр=Кэш.Данные.Найти(Значение, "Ключ");
   Если Стр=Неопределено Тогда
       Если Создавать Тогда
           Возврат обЗанестиВКэш(Кэш, Значение);
       КонецЕсли;
       Возврат Неопределено;
   КонецЕсли;
   Возврат Стр;
КонецФункции

//20071115 fixin
//Удаляет запись из кэша
Функция обУдалитьИзКэша(Кэш, Значение) Экспорт
   СтрКэша=обНайтиВКэше(Кэш, Значение);
   Если СтрКэша<>Неопределено Тогда
       Кэш.Данные.Удалить(СтрКэша);
   КонецЕсли;
КонецФункции

//20071115 fixin
//Не проверяет наличие в кэше, просто добавляет запись в кэш!!!
Функция обЗанестиВКэш(Кэш, Значение) Экспорт
   Стр=Кэш.Данные.Добавить();
   Стр.Ключ=Значение;
   Возврат Стр;
КонецФункции



//Параметры
//    Форма - форма
//    ОформлениеСтрок - оформление строк
//    Элемент - элемент списка
//    КолонкаКлюча - ключевое поле.
//    ДиспетчерВычислений - имя функции по вычислению данных
//    ДиспетчерВывода - имя функции по выводу данных

Функция обОтрисоватьКэш(П) Экспорт
   Перем Кэш;
   Перем Форма, ОформленияСтрок, Элемент, КолонкаКлюча, ДиспетчерВычислений, ДиспетчерВывода;
   П.Свойство("Форма", Форма);
   П.Свойство("ОформленияСтрок", ОформленияСтрок);
   П.Свойство("Элемент", Элемент);
   П.Свойство("КолонкаКлюча", КолонкаКлюча);
   П.Свойство("ДиспетчерВычислений", ДиспетчерВычислений);
   П.Свойство("ДиспетчерВывода", ДиспетчерВывода);
   Кэш=обВернутьКэшФормы(Форма, "КэшЭлементов");
   
   ВидимыеКолонки=Новый Массив();
   //Сначала смотрим, видимы ли колонки
   Для Каждого Эл ИЗ Кэш.Колонки Цикл
       Если Элемент.Колонки.Найти(Эл)=Неопределено Тогда
           Продолжить;
       КонецЕсли;
       ВидимыеКолонки.Добавить(Эл);
   КонецЦикла;
   //Оптимизация - если колонок кэша нет в списке, то пропускаем
   Если ВидимыеКолонки.Количество()=0 Тогда
       Возврат истина;
   КонецЕсли;
   
   //Нам нужно найти ссылки, для которых не найдено значение кэша
   ВычисляемыеСтроки=Новый Структура();
   ВычисляемыеКлючи=Новый Структура();
   Для Каждого Колонка из ВидимыеКолонки Цикл
       ВычисляемыеСтроки.Вставить(Колонка, Новый Массив());
       ВычисляемыеКлючи.Вставить(Колонка, Новый Массив());
   КонецЦикла;
   
   ЛокальныйКэш=Новый ТаблицаЗначений();
   ЛокальныйКэш.Колонки.Добавить("Ключ");
   ЛокальныйКэш.Колонки.Добавить("ОформлениеСтроки");
   ЛокальныйКэш.Колонки.Добавить("СтрокаКэша");
   
   //Смотрим, каких данных не хватает в кэше...
   Для Каждого ОформлениеСтроки ИЗ ОформленияСтрок Цикл
       ТекСтр=ОформлениеСтроки.ДанныеСтроки; //Строка оформления
       Ключ=ТекСтр[КолонкаКлюча];
       СтрокаКэша=обНайтиВКэше(Кэш, Ключ, истина); //Строка кэша
       СтрЛок=ЛокальныйКэш.Добавить();
       СтрЛок.Ключ=Ключ;
       СтрЛок.ОформлениеСтроки=ОформлениеСтроки;
       СтрЛок.СтрокаКэша=СтрокаКэша;
       Для Каждого Колонка из ВидимыеКолонки Цикл
           Ключ=ТекСтр[КолонкаКлюча];
           Если СтрокаКэша[Колонка]=Неопределено Тогда
               ВычисляемыеКлючи[Колонка].Добавить(Ключ);
               ВычисляемыеСтроки[Колонка].Добавить(СтрЛок);
           КонецЕсли;
       КонецЦикла;
   КонецЦикла;
   
   //Можно усовершенствовать - если есть невидимая колонка и она вдруг расчитывается
   //То ее тоже можно заносить в кэш, т.е. идти в том числе и по невидимым колонкам.
   
   //Вызываем диспетчера для вычисления значения колонки
   //Вычисляем значения колонки - в виде таблицы значений Ключ - Колонка1, Колонка2
   Для Каждого ИмяКолонкиВыч Из ВидимыеКолонки Цикл
       //Если все данные есть в кэше, ничего не делаем
       Если ВычисляемыеКлючи[ИмяКолонкиВыч].Количество()=0 Тогда
           Продолжить;
       КонецЕсли;
       
       //Вычисляем значения, которых не хватает
       ТЗ=Вычислить("Форма."+ДиспетчерВычислений+"(П, ИмяКолонкиВыч, ВычисляемыеКлючи[ИмяКолонкиВыч])");
       
       Если ТЗ=Неопределено Тогда
           ТЗ=Новый ТаблицаЗначений();
           ТЗ.Колонки.Добавить("Ключ");
           ТЗ.Колонки.Добавить(ИмяКолонкиВыч);
       КонецЕсли;
       
       ТЗ.Колонки.Добавить("СтрокаКэша");
       //Помечаем что колонки уже не нужны
       Для Каждого КолонкаТЗ ИЗ ТЗ.Колонки Цикл
           ИмяКолонки=КолонкаТЗ.Имя;
           Если НЕ ВычисляемыеСтроки.Свойство(ИмяКолонки) Тогда
               Продолжить;
           КонецЕсли;
           
           //Проставляем значения из ТЗ
           Для Каждого СтрТЗ ИЗ ТЗ Цикл
               СтрЛок=ЛокальныйКэш.Найти(СтрТЗ.Ключ, "Ключ");
               //Условие по идее никогда не должно сработать, но мало ли
               СтрЛок.СтрокаКэша[ИмяКолонки]=СтрТЗ[ИмяКолонки];
           КонецЦикла;
           
           //Если в ТЗ указаны значения не для всех ключей
           Если ТЗ.Количество()<ВычисляемыеКлючи[ИмяКолонки].Количество() Тогда
               Для Каждого СтрЛок ИЗ ЛокальныйКэш Цикл
                   Если СтрЛок.СтрокаКэша[ИмяКолонки]=Неопределено Тогда
                       СтрЛок.СтрокаКэша[ИмяКолонки]=NULL;
                   КонецЕсли;
               КонецЦикла;
           КонецЕсли;
           ВычисляемыеКлючи[ИмяКолонки].Очистить();
           
       КонецЦикла;
           
   КонецЦикла;
   
   //Теперь можем смело выводить все, что надо...
   Для Каждого СтрЛок ИЗ ЛокальныйКэш Цикл
       Для Каждого ИмяКолонки ИЗ ВидимыеКолонки Цикл
           Выполнить("Форма."+ДиспетчерВывода+"(П, СтрЛок.ОформлениеСтроки, СтрЛок.СтрокаКэша, ИмяКолонки)");
       КонецЦикла;
       Выполнить("Форма."+ДиспетчерВывода+"(П, СтрЛок.ОформлениеСтроки, СтрЛок.СтрокаКэша, Неопределено)");
   КонецЦикла;
   
КонецФункции
1 ТелепатБот
 
гуру
16.03.08
21:59
2 Гений 1С
 
гуру
16.03.08
22:01
(1) телепат, ты угадал: Книга знаний: v8: Размещение колонки с остатками в списке справочника
но я хотел сделать так, чтобы быстро разрабатывать код...
3 Asmody
 
модератор
17.03.08
00:03
(0) блин, ты б построитель запроса наконец изучил бы толком и не изобретал очередное квадратное колесо
4 Злопчинский
 
17.03.08
02:00
блин.. понятно, что здорово, но - здорово непонятно...
5 Kraft
 
17.03.08
07:24
генетально...
6 MaxS
 
17.03.08
08:35
(0) в типовых для этого используют не таблицу значений, а соответствие.
7 Гений 1С
 
гуру
17.03.08
09:23
(3) Асмоди, проясни свою светлую мысль.
(6) Не гони товарищ, там не сделано это таким образом, чтобы работа с кэшем занимала пару строчек, как у меня. Я унифицировал сей механизм...
Если хочешь поспорить, давай код в студию. А потом прикинь, сколько кода нужно еще написать, чтобы добавить еще одну вычислимую колонку. А у меня независимо от числа добавляемых колонок код простой - всего лишь меняются функции диспетчеров и все...
8 Господин ПЖ
 
17.03.08
09:46
А в 8.1 кеш работает? В списке при выводе строк весь список через "ОформлениеСтроки" не обрабатывается - только то что выводится на экран + еще пара строк... Если промотать список дальше - то что было выведено было раньше через "ОформлениеСтроки" теряется безвозвратно. Т.о. получается ситуация - данные в кеше есть, а списке на форме - нет. Есть ли смысл в таких кэшах?
9 Defender aka LINN
 
17.03.08
09:51
Мда... Весенне обострение.
Ждите новых и новых разновидностей велосипедов.
10 Гений 1С
 
гуру
17.03.08
09:51
(8) Есть конечно же есть... Выигрыш в производительности вывода на экран.
Можно очищать кэш скажем или каждые 30 секунд или после наполнения его до какого-то уровня, например на 10 000 позиций. ;-)

Кстати, в управляемом приложении источником для формы может быть компоновка данных (т.е. сложный запрос). Там кэш будет не нужен...

Я обычно делаю при закрытии формы или изменении данных рассылку широковещательных сообщений открытым формам, при получении которых кэш очищается, то бишь информация становится актуальной.

В любом случае юзверю приятнее нажать обновить, чем ЖДАТЬ.
11 Господин ПЖ
 
17.03.08
10:03
(10) >>Есть конечно же есть... Выигрыш в производительности вывода на экран.

Речь не о производительности. Речь о том что при неполном чтении списка и уничтожении в дальнейшем в нем уже прочитанного "смысла" в кэшах нет.
12 Гений 1С
 
гуру
17.03.08
10:12
(11) не уловил твою мысль. Что значит нет?
а если ты вверх-вниз проскроллируешь, т.е. вернешься к тем данным, что уже были показаны?
13 Господин ПЖ
 
17.03.08
10:15
(12) ты к ним уже можешь и не вернуться - они "затерты" новыми.
14 Гений 1С
 
гуру
17.03.08
10:18
(13) блин, что значит могу не вернуться:
АБВГДЕЖЗИЙКЛМНО
Допустим на экран выводится по 4 элемента
АБВГ - кэш АБВГ
нажимаю пждн:
ДЕЖЗ - кэш АБВГДЕЖЗ
нажимаю пждн:
ИЙКЛ - кэш АБВГДЕЖЗИЙКЛ
нажимаю пжап:
ДЕЖЗ - кэш АБВГДЕЖЗИЙКЛ (используется)
15 MaxS
 
17.03.08
10:45
(7) КонсольЗаданий.epf
ФормаКонсоли, переменная ФоновыеЗаданияСоответствие используется в качестве кэша...

а вообще, по моему необходимость в subj должна возникать достаточно редко. Если это не так, значит база данных неоптимальная, т.к. приходится постоянно что-то расчитывать, вместо того, чтобы просто взять из регистров... ;)

да и некогда мне спорить. я лучше поработаю ;)
16 КонецЕсли
 
17.03.08
11:05
(14) Он имеет в виду, что данные могут быть изменены другим пользователем, а тебе тебе будет отображаться устаревщее состояние - из твоего кеша.
17 Гений 1С
 
гуру
17.03.08
11:38
(16) Я же говорю - через рассылку широковещательных сообщений обновляю кэш.
(15) Неоптимальная гришь? и тут же пишешь что данные в регистрах хранятся. ;-)
Так вот, 1с позволяет выводить в форму только реквизиты объекта, остальное надо рассчитывать. И на файловой версии эти рассчеты тормозят по-любому.
18 MaxS
 
17.03.08
11:53
(17) в регистрах должно лежать всё посчитанное, а форма должна просто отображать результат.
Зачем каждому пользователю тыщю раз просчитывать(брать из кэша) одно и то же в форме? Если эти данные можно посчитать один раз при проведении документа.
19 Гений 1С
 
гуру
17.03.08
12:03
(18) В твоих словах есть резон... ;-)
Однако можно такую хрень, как у мя использовать для того, чтобы вытаскивать данные в том числе и из регистров, учитывая какие колонки видимые. Суть проджекта была в упрощении разработки таких форм - их у меня вагон и тележка.. ;-)