Přestože je v ASP.NET zavedení kešování direktivou @OutputCache velmi snadné, jednoduché možnosti nastavení závislosti keše jsou pouze základní. Pokud aplikace vyžaduje dodatečné závislosti keše například na základě rolí přihlášeného uživatele, pomůžeme si vlastní metodou. Pak můžeme jednoduše určovat závislost a platnost keše atributem VaryByCustom.

V aplikaci, kde potřebujeme @OutputCache učinit závislou na našich vlastních hodnotách (a ty mohou mít nejrůznější původ, od data posledního zápisu do souboru, přes změnu v databázi, až po údaje související s autorizací uživatele), využijeme atribut VaryByCustom. Díky vlastní metodě GetVaryByCustomString() pak budeme moci tento atribut využívat stejným způsobem, jako atribut VaryByParam, kde jednotlivé závislosti zapisujeme za sebou jednoduše oddělené středníkem.

Prohlédněte si příklad zápisu závislostí na URL, jazykovém nastavení, členství v roli a typu zařízení (zdrojový kód):

<%@ OutputCache Shared=“True“ Duration=“7200″ VaryByParam=“none“ VaryByCustom=“ContentUrl;Culture;Roles;DeviceType“ %>

Aby aplikace pozřela hodnotu uvedenou v atributu VaryByCustom a zachovala se, jak očekáváme, musíme přepsat chování metody GetVaryByCustomString(), která je obsažena ve třídě vdechující život celé HttpApplication prostřednictvím souboru Global.asax. Důmyslnost našeho řešení spočívá v tom, že můžeme jednotlivé položky, podle kterých se má řídit kešování, zadávat libovolně oddělené středníkem stejně, jako jsme zvyklí u atributů VaryByParam a VaryByControl. Většina podobných řešení pracuje pouze s jednou hodnotou, kterou pak testujeme uvnitř obslužné metody – více hodnot současně není možné uvést, nebo je nutno uvnitř obslužné metody komplikovaně rozdělovat vstupní řetězec na jednotlivé hodnoty. V našem řešení naproti tomu celý řetězec vůbec rozdělovat nebudeme, nebudou nás zajímat jednotlivé složky oddělené středníkem a nebudeme proto nadbytečně snižovat výkon aplikace – využijeme vyhledávací metodu IndexOf().

Příklad přepisu metody GetVaryByCustomString():

public override String GetVaryByCustomString(System.Web.HttpContext context, String arg)
{     
  if(!(arg != null && arg.Length > 0))
    return String.Empty;
  else
  {
    String compareArg = arg.ToLower();
    System.Text.StringBuilder cString = new System.Text.StringBuilder();
    if (compareArg.IndexOf(„contenturl“)>=0)
      cString.Append(context.Request.RawUrl.ToLower());
    if (compareArg.IndexOf(„culture“)>=0 && System.Threading.Thread.CurrentThread.CurrentUICulture.Name != null)
      cString.Append(System.Threading.Thread.CurrentThread.CurrentUICulture.Name);
    if (compareArg.IndexOf(„devicetype“)>=0 && context.Items[„DeviceType“] != null)
      cString.Append((string)context.Items[„DeviceType“]);
    if (compareArg.IndexOf(„roles“)>=0 && context.User.Identity.IsAuthenticated)
    {
      if (context.User is Interval.CZ.Web.Security.CustomPrincipal)
      {
        cString.Append(((Interval.CZ.Web.Security.CustomPrincipal)context.User).CommaSeparatedRoles);
      }
    }
    if (compareArg.IndexOf(„isauth“)>=0 && context.User.Identity.IsAuthenticated)
    {
      cString.Append(„isAuth“);
    }
    if (compareArg.IndexOf(„username“)>=0 && context.User.Identity.IsAuthenticated)
      cString.Append(context.User.Identity.Name);
    return cString.ToString();
  }
}

Povšimněte si, že pro sestavení výsledného řetězce používáme třídu StringBuilder, která je přímo stavěná pro skládání řetězců a nenese nevýhody prostého sčítání řetězců, jakožto odkazových typů. Jednotlivé části, podle kterých se má kešovat, však vyhledáváme přímo na vstupním řetězci metodou IndexOf(), je proto dobré zvážit počet závislostí, které chceme v metodě sledovat. Při větším počtu závislostí by totiž mohlo být vyhledávání v dlouhém řetězci pomocí IndexOf() i nepříjemně pomalé.

Jako bonus si ukážeme jednu málo známou možnost, jak monitorovat změny v libovolné tabulce v databázi, a to zejména ve starších verzích .NET (1.1, 1.0), kde není k dispozici SqlDependency pro určení závislosti na změnách v databázi. Nic nám nebrání se však rozhodnout tento způsob využít i v ASP.NET 2.0 aplikaci, a to zejména v případě, kdy nemáme k dispozici SQL Server 2005, který je pro SqlDependency optimalizován – starší verze sice také umožňují použít SqlDependency, nicméně za cenu někdy i velmi významného zatížení SQL serveru.

Změny tabulce databáze můžeme monitorovat pomocí souboru, který bude aktualizován automaticky díky triggeru připojenému k tabulce. Elegantně zde využijeme vestavěnou uloženou proceduru sp_makewebtaks.

CREATE TRIGGER MonitorArticles ON [dbo].[Articles] FOR
    INSERT, UPDATE, DELETE
AS
    EXEC sp_makewebtaks „\\machine1\CacheMonitor\Articles.src“, „SELECT GETDATE()“

Je potřeba si říct, že uživatel spouštějící proceduru musí mít nastavena dostatečná oprávnění a také účet SQL serveru musí mít právo zápisu do požadované složky – toto mohou být v některých situacích nevýhody, které znemožní použít tento jinak velmi jednoduchý způsob řízení kešování. Podotýkám, že při použití SQL Server 2005 bychom měli místo uvedené procedury raději využít Microsoft SQL Server Reporting Services (SSRS), uvedená procedura je zde k dispozici pouze z důvodů zpětné kompatibility a v následujících verzích již nebude.

A nyní zpět k řízení kešování. V naší metodě pro vrácení hodnoty použijeme jednoduše čas posledního zápisu do souboru prostřednictvím statické metody GetLastWriteTime():

System.IO.File.GetLastWriteTime(„\\\\machine1\\CacheMonitor\\Articles.src“).ToString();

Samozřejmě nám nic nebrání použít takto vytvářený soubor k řízení závislosti i pro vlastní kešování s využitím metody Insert():

Cache.Insert(„myDS“, DS, new CacheDendency(„<a href=’file://machine1/CacheMonitor/Articles.src‘>\\machine1\CacheMonitor\Articles.src</a>“));

Závěrem chci jen upozornit na možné bezpečnostní úskalí při kešování v aplikacích pracujících s citlivými údaji – nesnažte se překotně kešovat vždy a vše, pečlivě zvažujte, které údaje budou užitečné většině uživatelů, které jen několika málo a které smí vidět opravdu jen jediný!

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět