Программное очищение кеша Web User Controls для ASP.NET
В ASP.NET встроено несколько способов кеширования:
- Кеширование данных, например результатов «тяжёлых запросов»
- Кеширование результата Action'a для ASP.NET MVC
- Кеширование страниц целиком
- Кеширование Web User Controls
Для последних двух способов кешированием можно управлять декларативно с помощью аттрибутов и разметки в .aspx\.ascx так и программно. В ASP.NET 4.0 появилась возможность использовать собственные провайдеры (для кеширования данных, например, на диске).
К сожалению, встроенные средства кеширования не позволяют программно очистить кеш Control'ов, например очистить кеш для блока последних новостей после добавления новой записи. Способ того, как обойти это ограничение и будет описан в этом посте.
Идея метода заключается в следующем: напрямую управлять сбросом кеша контролов нельзя, но можно указать объекты в кеше, при удалении которых будет очищен кеш контрола.
Вариант реализации (для простоты демонстрации все контролы используют одну и ту же зависимость).
Помещение в кеш «вечного» объекта, от которого будут зависеть контролы
Это можно сделать, например, в Global.asax:
protected void Application_Start()
{
UtilsCache.InitControlCache(Resources.Cache.CacheKeyControls);
}
Реализация метода InitControlCache():
public static void InitControlCache(string cacheKey)
{
HttpContext.Current.Cache.Insert(cacheKey, DateTime.Now, null,
DateTime.MaxValue, TimeSpan.Zero,
CacheItemPriority.NotRemovable,
null);
}
Конфигурация Web User Control'a для кеширования
[PartialCaching(0)] //Необходимо, чтобы включить кеш
public partial class LatestNewsControl : System.Web.UI.UserControl
{
void Page_Init(object sender, System.EventArgs e)
{
UtilsCache.TuneUserControlCache(CachePolicy, Resources.Cache.CacheKeyControls, GetNumberOfSecondsForCachingThisControlFromConfig());
}
...
}
Реализация метода TuneUserControlCache():
public static void TuneUserControlCache(ControlCachePolicy cachePolicy, string cacheKey, int cacheTimeSeconds)
{
// Выключаем кеш, если это нужно
if (cacheTimeSeconds == 0 || GetValueFromConfigIfCacheEnabled())
{
cachePolicy.Cached = false;
}
// Эта часть кода вызывается если контрол не был закеширован
if (cachePolicy.SupportsCaching)
{
// Если по каким-то причинам "вечного" объекта в кеше нет -- добавляем его
if (HttpContext.Current.Cache[cacheKey] == null)
{
InitControlCache(cacheKey);
}
// Зависимость от "вечного" объекта в кеше
string[] keysDependency = {
cacheKey
};
// Можно добавить дополнительные зависимости, например от файлов
string[] filesDependency = {
HostingEnvironment.MapPath("~/Web.config"),
};
CacheDependency dependency = new CacheDependency(filesDependency, keysDependency);
cachePolicy.Duration = TimeSpan.FromSeconds(cacheTimeSeconds);
cachePolicy.Dependency = dependency;
}
}
Програмная очистка кеша
Для очистки кеша контролов достаточно удалить «вечный» объект их кеша:
HttpContext.Current.Cache.Remove(Resources.Cache.CacheKeyControls);
Лирическое отступление об ASP.NET MVC
ASP.NET MVC использует имеющиеся методы кеширования, которые конфигурируются сходным образом. До выхода ASP.NET MVC 2 для кеширования части страницы, например, блока меню, списка заголовков последних новостей приходилось использовать Web User Control, например способом описанным в посте Donut Hole Caching in ASP.NET MVC (в случае, если испольуется WebForms View Engine).
В этой ситуации традиционные Web User Control'ы с кодом в codebehind-файлах можно было использовать в качестве viewlet'ов (другие названия subaction, partial request) — т.е. для вывода какой-то информации на нескольких страница в обход MVC. Типичный пример такого использования — меню на каждой странице сайта. Для этой ситуации описываем подход вполне применим.
В ASP.NET MVC 2 появились методы Html.Action() и Html.RenderAction() и проблема с управлением кешированием решается гораздо проще с помощью определения собственного аттрибута.
