Программное очищение кеша Web User Controls для ASP.NET

02.05.2010 13:57 / Артём Волк / 1723 просмотра / ...

В 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() и проблема с управлением кешированием решается гораздо проще с помощью определения собственного аттрибута.