При створенні веб додатків часто виникають завдання кешування даних для швидкого доступу до них. За часів ASP для кешування даних використовувався об'єкт Application. Цей підхід давав зручний спосіб збереження об'єктів і доступу до них з будь-якої веб сторінки додатка, але при цьому він також містив у собі деякі серйозні проблеми, як то потенційну можливість вичерпання доступної пам'яті при активному збереженні об'єктів в Application і проблеми відстеження змін даних і оновлення об'єктів зі зміненими даними.
Даного питання (використання об'єкта Application як кеша об'єктів) було присвячено безліч обговорень і статей монстрів класичного ASP. І, природно, дана проблема не могла бути обійдена Microsoft при розробці ASP.NET. І в підсумку було запропоновано просте і елегантне рішення.
У ASP.NET крім класу HttpApplication був введений додатковий клас HttpCache спеціально для створення кеша об'єктів. Робота з цим класом дуже схожа на роботу з Application, але є й істотні відмінності. В першу чергу ці відмінності спрямовані на вирішення зазначених вище проблем:
- Управління пам'яттю - кеш може автоматично видаляти об'єкти для небопущенія переповнення пам'яті.
- Знищення об'єктів при настанні деякої події - об'єкт може бути поміщений в кеш з правилами його видалення.
- Callback виклики - кеш підтримує можливість виклику методів при видаленні об'єктів з кеша.
Доступ до кешу здійснюється через властивість Cache поточного контексту веб додатки. Поміщати об'єкти в кеш можна як неявно (так само, як і при роботі з Application), так і за допомогою методів Add і Insert. Робота даних методів абсолютно ідентична і відрізняється тільки в повертаються значеннях (метод Add повертає посилання на доданий в кеш об'єкт).
Розглянемо найпростіший приклад використання кеша об'єктів. Отриманий з джерела даних результат запиту помістимо в кеш і будемо виводити цей результат на сторінку. Також будемо виводити інформацію про те, звідки береться результат - з кеша або з джерела даних. Нижче наведено код обробника події Load сторінки, що виконує ці дії:
В даному прикладі робота з об'єктом Cache дуже схожа на роботу з Application. Але з однією істотною відмінністю - перед отриманням об'єкта з кеша необхідно ЗАВЖДИ перевіряти наявність об'єкта! Приміщення об'єкта в кеш (нехай навіть 2 секунди назад) не дає гарантії, що цей об'єкт все ще є там (в цьому полягає найважливіша відмінність Cache і Application).
Неявне приміщення об'єкта в кеш в наведеному прикладі ідентично наступного виклику: Cache.Insert ( "dataset", ds). Але метод Insert має 4 перевантажених варіанти, за допомогою яких можна управляти правилами зберігання об'єкта в кеші (адже саме для цього і був створений кеш :)).
Розглянемо наступний приклад. На сайті електронного магазину на багатьох сторінках виводиться інформація про лідерів продажів. Джерелом цієї інформації є XML файл, який може змінюватися в багатьох місцях. Так як доступ до цієї інформації потрібен часто - краще її звичайно ж зберігати в кеші. Ну а відстеженням змін в даних нехай той же кеш і займається - благо дана можливість була додана розробниками з Microsoft :).
Для контролю незмінності файлу використовується клас System.Web.Caching.CacheDependency. Якщо необхідно додати цю функціональність до об'єкта, помещаемому в кеш, потрібно створити екземпляр класу CacheDependency і передати його в якості параметра в метод Cache.Insert:
Тепер можна повернутися і до реалізації прикладу. Код даного прикладу наведено нижче:
Для перевірки роботи процесу зберігання об'єкта в кеші і видалення його при зміні файлу я також додав код, який додає дані в xml файл. При першому запуску сторінки дані будуть отримані з файлу, а потім буде використовуватися інформація з кеша. При додаванні даних я не виробляю ніяких спеціальних дій для видалення об'єкта з кеша, але модифікація даних буде виявлена і зберігається в кеші об'єкт буде видалений.
Клас System.Web.Caching.CacheDependency має 8 перевантажених конструкторів, за допомогою яких можна вказувати відстеження змін окремого файлу, каталогу, списку файлів і каталогів, а також зміна інших об'єктів в кеші (тобто об'єкт може бути видалений в залежності від зміни значення іншого об'єкта в кеші) і залежність від іншого об'єкта типу CacheDependency. Також для всіх наведених вище умов можна встановити час, починаючи з якого необхідно відстежувати ці зміни. Більш детальну інформацію про всі перевантаженнях конструктора класу CahceDependency можна знайти в MSDN.
Тепер розглянемо інший варіант кешування даних. Ті ж дані, що і в попередньому прикладі, тепер досить таки активно змінюються. При цьому імовірний певний неактуальність даних, що виводяться (швидкість доступу понад усе). Кращим алгоритмом в даному випадку було б оновлення даних в кеші через певний проміжок часу (наприклад кожні 5 хвилин). Дане завдання також цілком під силу кешу.
Проміжок часу, протягом якого об'єкт буде зберігатися в кеші, може бути як абсолютним (установка точного часу, після якого об'єкт буде видалений з кеша), так і змінним (час зберігання об'єкта змінюється кожен раз при доступі до цього об'єкта). Розглянемо обидва способи:
У цьому прикладі об'єкт з даними поміщається в кеш на 5 хвилин (якщо використовувати рядок 1) або на 2 хвилини з моменту останнього доступу до об'єкта (для рядка 2). Також в цьому коді показаний приклад використання залежності одного об'єкта в кеші від іншого (я зберігаю в кеші поточний час і встановлюю залежність цього об'єкта від об'єкта з даними). При зміні даних об'єкт в кеші не буде видалений до тих пір, поки не закінчиться час його зберігання. При цьому природно виникає неузгодженість даних, але, за умовами даного завдання, це допустимо. Даний спосіб кешування даних (збереження об'єкта в кеші на певний проміжок часу) найкраще підходить при кешування об'єктів, які зберігають дані з баз даних. На жаль розробники Microsoft так і не зробили можливість установки залежно об'єктів в кеші від стану даних в джерелі даних, хоча це і анонсувалося.
Необхідно також згадати про один важливий обмеження у використанні установки залежно зберігання об'єкта від часу - для одного об'єкта можна використовувати тільки один тип часу. При установці абсолютного часу зберігання об'єкта в кеші другий параметр повинен мати значення System.Web.Caching.Cache.NoSlidingExpiration (або TimeSpan.Zero що суть одне і те ж). Відповідно при використанні змінного часу зберігання об'єкта в кеші перший параметр повинен бути рівний System.Web.Caching.Cache.NoAbsoluteExpiration (або DateTime.MaxValue). При невиконанні цієї умови буде згенеровано виняток.
Як я вже згадував раніше, кеш може сам керувати пам'яттю і видаляти об'єкти в разі досягнення граничного значення. Сам алгоритм процедури видалення об'єктів прихований від наших очей, але можливість деякого впливу на нього є. При додаванні об'єкта в кеш є можливість вказати пріоритет зберігання об'єкта (тобто об'єкти з більш низьким пріоритетом будуть видалені з кешу швидше, ніж об'єкти з високим пріоритетом). Також можна заборонити видаляти об'єкт з кеша.
Для вказівки пріоритету зберігання об'єкта в кеші використовується перерахування System.Web.Caching.CacheItemPriority, що має наступні значення (в порядку зростання пріоритету): Low, BelowNormal, Default, Normal, AboveNormal, High, NotRemovable. Об'єкт, доданий в кеш з пріоритетом CacheItemPriority.NotRemovable, що не буде віддалятися при очищенні кеша ніколи. Останній параметр, який може приймати метод Insert при вставці об'єкта в кеш, має тип System.Web.Caching.CacheItemRemovedCallback. Цей параметр приймає в якості значення делегата, що викликається при видаленні об'єкта з кеша. Ви можете створити обробник наприклад для оповіщення додатки про об'єкт рухається у зворотному з кеша.
Як і в минулих прикладах наводиться тільки цікавий для нас код:
Не завжди зручно чекати моменту видалення об'єкту з кеша по доданим правилам. Наприклад при досить великому оновленні бази даних добре було б також видалити з кеша об'єкти, що зберігають стару інформацію. Для видалення об'єктів з кеша призначений метод Remove, що приймає один параметр - ім'я об'єкта в кеші.
Остання функціональність класу System.Web.Caching.Cache, на яку хотілося б звернути вашу увагу, це реалізація інтерфейсу IEnumerable, що дозволяє швидко і зручно отримувати доступ до всіх об'єктів, що зберігаються в кеші програми.
Підводячи підсумки всього вищесказаного можна сказати наступне - правильне використання Cache API дозволяє підвищити швидкодію веб додатки (у багатьох випадках - в рази). Cache API представляє зручний інтерфейс для зберігання даних в кеші разом з інформацією про час зберігання об'єктів. Кешіруя редкоізменяемие дані з джерела даних можна різко знизити навантаження на сервера баз даних і підвищити швидкість відгуку. Але як і в інших подібних випадках використанням Cache API не слід зловживати.