Caching i ASP.NET

Internettbrukere er utålmodige og dersom ting går tregt forsvinner brukerne fort. For å lage raske applikasjoner med god ytelse er det mange ting man bør tenke på og en av de er caching.

I .NET er caching ganske enkelt å implementere. Det finnes hovedsaklig 2 måter å gjøre dette på. Man kan legge inn caching i selve aspx-filen sin (evt usercontrol ascx-filer) eller man kan manuelt cache i selve koden. I denne artikkelen skal vi ta en kikk på begge disse måtene å cache på.

Cache i page direktivet

Den enkleste måten å cache på er å putte inn en OutputCache i page direktivet. (øverst i aspx-filen). Dene metode gjør at hele siden blir cachet og dermed kan man spare mye databasetrafikk.

<%@OutputCache Duration="60" VaryByParam="none" %>

Du må lagge inn 2 parametre for å få denne til å fungere. Duration angir (i sekunder) hvor lenge siden skal caches. Med VaryByParam kan man angi at det skal caches en ulik utgave av siden basert på variabler i querystring. Hvis man har en side som viser et produkt så er det ganske vanlig å angi feks produktId i querystring’en. For at ikke begge sidene skal caches som samme dokument må man benytte VaryByParam.

<%@OutputCache Duration="60" VaryByParam="prodId" %>

Med koden over vil sidene http://minside.no/produkt.aspx?prodId=123 og http://minside.no/produkt.aspx?prodId=789 vises ulikt selv om de caches i samme dokument.

Et enkelt eksempel på bruk av OutPutCache kan man lage vha en side som skriver ut hvor mye klokken er:

<%@Page Language="C#" %>
<%@OutputCache Duration="30" VaryByParam="none" %>
<html>
<head>
    <title>Cache test</title>
</head>
<body>
    <form id="form1" runat="server">
        <%=DateTime.Now %>
    </form>
</body>
</html>

I dette eksempelet vil klokken kun oppdateres hvert 30. sekund. Den vil ellers oppføre seg som en helt statisk html-fil. Hvis du endrer VaryByParam til feks test så vil cachetest.aspx?test=1 og cachetest.aspx?test=2 caches hver for seg.

Cache i koden

Når man lager webapplikasjoner bør man følge en form for arkitektur. I en 3-lags arkitektur har man et eget lag som håndterer data. Allerede i dette kodelaget kan man cache data. Dette er hakket mer komplisert enn OutPutCaching, men definitivt overkommelig.

Hvis du har en funksjon som returnerer et DataSet kan man cache dette DataSet’et slik at men ikke trenger å hente dataene fra databasen hver eneste gang denne funksjonan kalles. En vanlig funksjon (uten caching) som henter data fra databasen, putter det en en DataTable og returnerer det:

protected void Page_Load(object sender, EventArgs e)
{
    this.dtg.DataSource = GetData();
    this.dtg.DataBind();
}

public DataTable GetData()
{
    SqlConnection conn = new SqlConnection(ConnString);
    SqlCommand cmd = new SqlCommand("SELECT TOP 10 * Table", conn);
    conn.Open();
    SqlDataAdapter da = new SqlDataAdapter(cmd);
    DataTable table = new DataTable("MinDatatable");
    da.Fill(table);
    conn.Close();

    return table;
}

Dette kodeeksempelet fungerer fint, men hver eneste gang side lastes så hentes dataene fra databasen. Dersom dataene ikke endrer seg ofte så trenger man heller ikke å lese av dataene hele tiden. For enkelhets skyld lager jeg en egen funksjon som først sjekker om dataene allerede er cachet og hvis de ikke er det så henter jeg data fra databasen via funksjonen GetData()

public DataTable GetCachedData()
{
    System.Web.Caching.Cache cache = System.Web.HttpRuntime.Cache;
    // Tar ut data fra cachen
    DataTable table = (DataTable)cache.Get("minTabell");
    
    // Sjekker om det var noe data der
    if (table == null)
    {
        // Fant ikke data i cachen. Hent fra database og putt i cachen.
        table = GetData();
        cache.Insert("minTabell", table, null, DateTime.Now.AddMinutes(30), Caching.Cache.NoSlidingExpiration);
    }
    
    // Returner DataTable
    return table;
}

Når man bruker denne metoden er det et par ting variabler man bør kjenne til:

  • AbsoluteExpiration:
    I eksempelt over bruker jeg AbsoluteExpiration. Da angir du et spesifikt tidspunkt for når cache-objektet skal slettes. I eksempelet legger jeg inn verdien DateTime.Now.AddMinutes(30) som er et absolutt tidspunkt (om 30 minutter)
  • SlidingExpiration:
    Dette kan man angi dersom man vil at cache-objektet skal slettes dersom det har vært innaktivt i en viss tid. SlidingExpiration oppgis med en TimeSpan. Feks new TimeSpan(0, 30, 0) (30 minutter).
  • CacheDependency:
    Cacheing kan brukes til mer enn databaser. Man kan feks cache innholdet i en fil. Dersom filen endres vil man gjerne slette dataene i cachen. Dette kan gjres vha CacheDependency. Ved å angi new System.Web.Caching.CacheDependency(“C:\\minfil.xml”) vil cacheobjektet slettes hvhis filen C:\\minfil.xml endres.

Dersom man endrer web.config eller gjør en recycle i IIS vil alle cacheobjekter slettes.