Dela via


Cachelagring av data vid programstart (C#)

av Scott Mitchell

Ladda ned PDF

I alla webbprogram används vissa data ofta och vissa data används sällan. Vi kan förbättra prestandan för vårt ASP.NET-program genom att i förväg läsa in de data som används ofta, en teknik som kallas cachelagring. Den här handledningen visar en metod för proaktiv laddning, som är att ladda data i cacheminnet vid programstart.

Inledning

De två tidigare självstudierna tittade på cachelagring av data i Presentationslagret och Cachelagret. I Cachelagring av data med ObjectDataSource tittade vi på hur du använder ObjectDataSources cachelagringsfunktioner för att cachelagra data i presentationslagret. Cachelagring av data i arkitekturen undersökte cachelagring i ett nytt, separat cachelagringslager. Båda dessa handledningar använde reaktiv inläsning i arbetet med datacachen. Vid reaktiv inläsning kontrollerar systemet först om de finns i cacheminnet varje gång data begärs. Annars hämtar den data från den ursprungliga källan, till exempel databasen, och lagrar dem sedan i cacheminnet. Den största fördelen med reaktiv inläsning är att den är enkel att implementera. En av nackdelarna är dess ojämna prestanda för begäranden. Föreställ dig en sida som använder cachelagringsskiktet från föregående självstudiekurs för att visa produktinformation. När den här sidan besöks för första gången eller besöks första gången efter att cachelagrade data har avlägsnats på grund av minnesbegränsningar eller att den angivna förfallotiden har nåtts, måste data hämtas från databasen. Därför tar dessa användarbegäranden längre tid än användarnas begäranden som kan hanteras av cachen.

Proaktiv inläsning ger en alternativ strategi för cachehantering som jämnar ut prestanda mellan begäranden genom att läsa in cachelagrade data innan de behövs. Vanligtvis använder proaktiv inläsning en viss process som antingen regelbundet kontrollerar eller meddelas när det har skett en uppdatering av underliggande data. Den här processen uppdaterar sedan cachen för att hålla den fräsch. Proaktiv inläsning är särskilt användbart om underliggande data kommer från en långsam databasanslutning, en webbtjänst eller någon annan särskilt trög datakälla. Men den här metoden för proaktiv inläsning är svårare att implementera eftersom den kräver att du skapar, hanterar och distribuerar en process för att söka efter ändringar och uppdatera cacheminnet.

En annan variant av proaktiv inläsning, och den typ som vi ska utforska i den här handledningen, är att läsa in data i cacheminnet vid programstart. Den här metoden är särskilt användbar för cachelagring av statiska data, till exempel poster i databassökningstabeller.

Anmärkning

En mer ingående titt på skillnaderna mellan proaktiv och reaktiv inläsning samt listor över rekommendationer för fördelar, nackdelar och implementering finns i avsnittet Hantera innehållet i en cache i guiden för cachelagringsarkitektur för .NET Framework-program.

Steg 1: Fastställa vilka data som ska cachelagrats vid programstart

Cachelagringsexemplen med reaktiv inläsning som vi undersökte i de föregående två självstudierna fungerar bra med data som regelbundet kan ändras och som inte tar orimligt lång tid att generera. Men om cachelagrade data aldrig ändras är förfallodatum som används vid reaktiv inläsning överflödigt. På samma sätt, om data som cachelagras tar mycket lång tid att generera, måste de användare vars begäranden hittar cachen tom uthärda en lång väntan medan underliggande data hämtas. Överväg att cachelagra statiska data och data som tar exceptionellt lång tid att generera vid programstart.

Även om databaser har många dynamiska, ofta föränderliga värden, har de flesta också en hel del statiska data. Till exempel har praktiskt taget alla datamodeller en eller flera kolumner som innehåller ett visst värde från en fast uppsättning alternativ. En Patients databastabell kan ha en PrimaryLanguage kolumn vars uppsättning värden kan vara engelska, spanska, franska, ryska, japanska och så vidare. Ofta implementeras dessa typer av kolumner med hjälp av uppslagstabeller. I stället för att lagra strängen engelska eller franska i Patients tabellen skapas en andra tabell som vanligtvis har två kolumner – en unik identifierare och en strängbeskrivning – med en post för varje möjligt värde. Kolumnen PrimaryLanguage i Patients tabellen lagrar motsvarande unika identifierare i uppslagstabellen. I bild 1 är patienten John Doe primärspråk engelska, medan Ed Johnsons är ryska.

Språktabellen är en uppslagstabell som används av patienttabellen

Bild 1: Tabellen Languages är en uppslagstabell som används av tabellen Patients

Användargränssnittet för att redigera eller skapa en ny patient skulle innehålla en listruta med tillåtna språk, som hämtas från posterna i tabellen Languages. Utan cachelagring måste systemet köra frågor mot tabellen varje gång det här gränssnittet besöks Languages . Detta är slösaktigt och onödigt eftersom uppslagstabellvärden ändras mycket sällan, om någonsin.

Vi kan cachelagra Languages data med samma reaktiva inläsningstekniker som undersöktes i de föregående handledningarna. Reaktiv inläsning använder dock en tidsbaserad förfallotid, vilket inte behövs för statiska uppslagstabelldata. Även om cachelagring med reaktiv inläsning skulle vara bättre än ingen cachelagring alls, skulle den bästa metoden vara att proaktivt läsa in uppslagstabelldata i cacheminnet vid programstart.

I den här handledningen ska vi titta på hur man cachelagrar uppslagstabelldata och annan statisk information.

Steg 2: Undersöka de olika sätten att cachelagrar data

Information kan cachelagras programmatiskt i ett ASP.NET program med hjälp av en mängd olika metoder. Vi har redan sett hur du använder datacachen i tidigare självstudier. Alternativt kan objekt cachelagras programmatiskt med statiska medlemmar eller programtillstånd.

När du arbetar med en klass måste klassen vanligtvis först instansieras innan dess medlemmar kan nås. För att kunna anropa en metod från en av klasserna i vårt affärslogiklager måste vi till exempel först skapa en instans av klassen:

ProductsBLL productsAPI = new ProductsBLL();
productsAPI.SomeMethod();
productsAPI.SomeProperty = "Hello, World!";

Innan vi kan anropa SomeMethod eller arbeta med SomeProperty måste vi först skapa en instans av klassen med nyckelordet new . SomeMethod och SomeProperty är associerade med en viss instans. Livslängden för dessa medlemmar är knuten till livslängden för deras associerade objekt. Statiska medlemmar är å andra sidan variabler, egenskaper och metoder som delas mellan alla instanser av klassen och därmed har en livslängd så länge som klassen. Statiska medlemmar anges med nyckelordet static.

Förutom statiska medlemmar kan data cachelagras med hjälp av programtillstånd. Varje ASP.NET program har en namn-/värdesamling som delas mellan alla användare och sidor i programmet. Den här samlingen kan nås med hjälp av HttpContext klassensApplication egenskap och användas från kod bakom klassen på en ASP.NET sida så här:

Application["key"] = value;
object value = Application["key"];

Datacachen ger ett mycket mer omfattande API för cachelagring av data, som tillhandahåller mekanismer för tids- och beroendebaserade förfallodatum, prioriteringar för cacheobjekt och så vidare. Med statiska medlemmar och programtillstånd måste sådana funktioner läggas till manuellt av sidutvecklaren. Vid cachelagring av data vid programstart för programmets livslängd är dock datacachens fördelar irrelevanta. I den här självstudien tittar vi på kod som använder alla tre teknikerna för cachelagring av statiska data.

Steg 3: CacheSupplierstabell data

De Northwind-databastabeller som vi har implementerat hittills innehåller inga traditionella uppslagstabeller. De fyra DataTables som implementeras i vårt DAL modellerar alla tabeller vars värden inte är statiska. I stället för att lägga till en ny DataTable i DAL och sedan en ny klass och metoder till BLL: n, för den här självstudien ska vi bara låtsas att Suppliers tabellens data är statiska. Därför kan vi cachelagra data vid programstart.

Börja genom att skapa en ny klass med namnet StaticCache.cs i CL mappen .

Skapa klassen StaticCache.cs i CL-mappen

Bild 2: Skapa StaticCache.cs klassen i mappen CL

Vi måste lägga till en metod som läser in data vid start till lämpligt cachelager samt metoder som returnerar data från den här cachen.

[System.ComponentModel.DataObject]
public class StaticCache
{
    private static Northwind.SuppliersDataTable suppliers = null;
    public static void LoadStaticCache()
    {
        // Get suppliers - cache using a static member variable
        SuppliersBLL suppliersBLL = new SuppliersBLL();
        suppliers = suppliersBLL.GetSuppliers();
    }
    [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
    public static Northwind.SuppliersDataTable GetSuppliers()
    {
        return suppliers;
    }
}

Koden ovan använder en statisk medlemsvariabel, suppliers, för att lagra resultatet från SuppliersBLL-klassens GetSuppliers()-metod, som anropas från LoadStaticCache()-metoden. Metoden LoadStaticCache() är avsedd att anropas under programmets start. När dessa data har lästs in vid programstart kan alla sidor som behöver arbeta med leverantörsdata anropa StaticCache klassens GetSuppliers() metod. Därför sker anropet till databasen för att hämta leverantörerna bara en gång, vid programstart.

I stället för att använda en statisk medlemsvariabel som cachelagring kunde vi alternativt ha använt applikationstillstånd eller datacache. Följande kod visar klassen som har omarbetats för att använda programtillstånd:

[System.ComponentModel.DataObject]
public class StaticCache
{
    public static void LoadStaticCache()
    {
        // Get suppliers - cache using application state
        SuppliersBLL suppliersBLL = new SuppliersBLL();
        HttpContext.Current.Application["key"] = suppliersBLL.GetSuppliers();
    }
    [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
    public static Northwind.SuppliersDataTable GetSuppliers()
    {
        return HttpContext.Current.Application["key"] as Northwind.SuppliersDataTable;
    }
}

I LoadStaticCache()lagras leverantörsinformationen i programvariabelnyckeln. Den returneras som lämplig typ (Northwind.SuppliersDataTable) från GetSuppliers(). Programtillstånd kan nås i kod-bakom klasserna för ASP.NET-sidor med hjälp av Application["key"], men i arkitekturen måste vi använda HttpContext.Current.Application["key"] för att hämta den aktuella HttpContext.

På samma sätt kan datacachen användas som ett cachelager, som följande kod visar:

[System.ComponentModel.DataObject]
public class StaticCache
{
    public static void LoadStaticCache()
    {
        // Get suppliers - cache using the data cache
        SuppliersBLL suppliersBLL = new SuppliersBLL();
        HttpRuntime.Cache.Insert(
          /* key */                "key", 
          /* value */              suppliers, 
          /* dependencies */       null, 
          /* absoluteExpiration */ Cache.NoAbsoluteExpiration, 
          /* slidingExpiration */  Cache.NoSlidingExpiration, 
          /* priority */           CacheItemPriority.NotRemovable, 
          /* onRemoveCallback */   null);
    }
    [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
    public static Northwind.SuppliersDataTable GetSuppliers()
    {
        return HttpRuntime.Cache["key"] as Northwind.SuppliersDataTable;
    }
}

Om du vill lägga till ett objekt i datacachen utan tidsbaserad förfallotid använder System.Web.Caching.Cache.NoAbsoluteExpiration du värdena och System.Web.Caching.Cache.NoSlidingExpiration som indataparametrar. Den här specifika överbelastningen av datacachens metod har valts Insert för att vi kan specificera prioriteten för cacheobjektet. Prioriteten används för att fastställa vilka objekt som ska rensas från cacheminnet när det tillgängliga minnet tar slut. Här använder vi prioriteten NotRemovable, vilket säkerställer att det här cacheobjektet inte rensas.

Anmärkning

Den här självstudien nedladdning implementerar StaticCache-klassen med hjälp av ansatsen för statisk medlemsvariabel. Koden för programtillståndet och datacachetekniken är tillgänglig i kommentarerna i klassfilen.

Steg 4: Köra kod vid programstart

För att köra kod när ett webbprogram startar först måste vi skapa en särskild fil med namnet Global.asax. Den här filen kan innehålla händelsehanterare för program-, sessions- och begärandenivåhändelser, och det är här vi kan lägga till kod som körs när programmet startar.

Lägg till filen i Global.asax webbprogrammets rotkatalog genom att högerklicka på namnet på webbplatsprojektet i Solution Explorer i Visual Studio och välja Lägg till nytt objekt. I dialogrutan Lägg till nytt objekt väljer du objekttypen Global programklass och klickar sedan på knappen Lägg till.

Anmärkning

Om du redan har en Global.asax fil i projektet visas inte objekttypen Global programklass i dialogrutan Lägg till nytt objekt.

Lägg till filen Global.asax i rotkatalogen för ditt webbprogram

Bild 3: Lägg till filen i rotkatalogen Global.asax för ditt webbprogram (Klicka om du vill visa en bild i full storlek)

Global.asax Standardmall för -filer innehåller fem metoder inom en tagg på server-sidan<script>:

  • Application_Start körs när webbappen först startar
  • Application_End körs när programmet stängs av
  • Application_Error körs när ett ohanterat undantag når programmet
  • Session_Start körs när en ny session skapas
  • Session_End körs när en session har upphört att gälla eller avbryts

Händelsehanteraren Application_Start anropas bara en gång under programmets livscykel. Programmet startar första gången en ASP.NET resurs begärs från programmet och fortsätter att köras tills programmet startas om, vilket kan inträffa genom att ändra innehållet /Bin i mappen, ändra Global.asax, ändra innehållet i App_Code mappen eller ändra Web.config filen, bland annat. Mer information om programmets livscykel finns i ASP.NET Översikt över programlivscykeln .

I dessa självstudier behöver vi bara lägga till kod i Application_Start-metoden, var god och ta bort de andra. I Application_Startanropar StaticCache du helt enkelt klassens LoadStaticCache() -metod, som läser in och cachelagr leverantörsinformationen:

<%@ Application Language="C#" %>
<script runat="server">
    void Application_Start(object sender, EventArgs e) 
    {
        StaticCache.LoadStaticCache();
    }
</script>

Det är allt! Vid programstart LoadStaticCache() hämtar metoden leverantörsinformationen från BLL:n och lagrar den i en statisk medlemsvariabel (eller vilket cachelager du än använde i StaticCache klassen). Om du vill verifiera det här beteendet anger du en brytpunkt i Application_Start metoden och kör programmet. Observera att brytpunkten aktiveras när programmet startas. Efterföljande begäranden gör Application_Start dock inte att metoden körs.

Använd en brytpunkt för att kontrollera att Application_Start händelsehanteraren körs

Bild 4: Använd en brytpunkt för att kontrollera att Application_Start händelsehanteraren körs (Klicka om du vill visa en bild i full storlek)

Anmärkning

Om du inte når Application_Start brytpunkten när du börjar felsöka beror det på att programmet redan har startats. Tvinga programmet att starta om genom att ändra dina Global.asax filer eller Web.config filer och försök sedan igen. Du kan helt enkelt lägga till (eller ta bort) en tom rad i slutet av en av dessa filer för att snabbt starta om programmet.

Steg 5: Visa cachelagrade data

I det här läget StaticCache har klassen en version av leverantörsdata som cachelagras vid programstart som kan nås via dess GetSuppliers() metod. För att arbeta med dessa data från presentationslagret kan vi använda en ObjectDataSource eller programmatiskt anropa StaticCache klassens GetSuppliers() -metod från en ASP.NET-sidans kod bakom-klass. Nu ska vi titta på hur du använder kontrollerna ObjectDataSource och GridView för att visa den cachelagrade leverantörsinformationen.

Börja med att öppna sidan AtApplicationStartup.aspx i Caching mappen. Dra en GridView från verktygslådan till designern och ange dess ID egenskap till Suppliers. Från GridViews smarta tagg väljer du sedan att skapa en ny ObjectDataSource med namnet SuppliersCachedDataSource. Konfigurera ObjectDataSource så att den StaticCache använder klassens GetSuppliers() -metod.

Konfigurera ObjectDataSource för att använda StaticCache-klassen

Bild 5: Konfigurera ObjectDataSource att använda StaticCache klassen (Klicka om du vill visa en bild i full storlek)

Använd metoden GetSuppliers() för att hämta cachelagrade leverantörsdata

Bild 6: Använd GetSuppliers() metoden för att hämta cachelagrade leverantörsdata (Klicka om du vill visa en bild i full storlek)

När du har slutfört guiden lägger Visual Studio automatiskt till BoundFields för vart och ett av datafälten i SuppliersDataTable. GridView och ObjectDataSources deklarativa markering bör se ut ungefär så här:

<asp:GridView ID="Suppliers" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="SupplierID" DataSourceID="SuppliersCachedDataSource" 
    EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="SupplierID" HeaderText="SupplierID" 
            InsertVisible="False" ReadOnly="True" 
            SortExpression="SupplierID" />
        <asp:BoundField DataField="CompanyName" HeaderText="CompanyName" 
            SortExpression="CompanyName" />
        <asp:BoundField DataField="Address" HeaderText="Address" 
            SortExpression="Address" />
        <asp:BoundField DataField="City" HeaderText="City" 
            SortExpression="City" />
        <asp:BoundField DataField="Country" HeaderText="Country" 
            SortExpression="Country" />
        <asp:BoundField DataField="Phone" HeaderText="Phone" 
            SortExpression="Phone" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="SuppliersCachedDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetSuppliers" TypeName="StaticCache" />

Bild 7 visar sidan när den visas via en webbläsare. Utdata är desamma om vi hade hämtat data från BLL-klassen SuppliersBLL , men med hjälp av StaticCache klassen returneras leverantörsdata som cachelagrade vid programstart. Du kan ange brytpunkter i StaticCache klassens GetSuppliers() metod för att verifiera det här beteendet.

Cachelagrade leverantörsdata visas i en GridView

Bild 7: Cachelagrade leverantörsdata visas i en GridView (Klicka om du vill visa en bild i full storlek)

Sammanfattning

De flesta datamodeller innehåller en hel del statiska data, vanligtvis implementerade i form av uppslagstabeller. Eftersom den här informationen är statisk finns det ingen anledning att kontinuerligt komma åt databasen varje gång den här informationen behöver visas. På grund av sin statiska natur behövs ingen utgångsdatum när data cachas. I den här handledningen såg vi hur du tar denna typ av data och cachelagrar dem i datacacheminnet, programtillståndet och via en statisk medlemsvariabel. Den här informationen cachelagras vid programstart och finns kvar i cacheminnet under programmets livslängd.

I den här självstudien och de två senaste har vi tittat på cachelagring av data under programmets livslängd och använt tidsbaserade förfallodatum. Vid cachelagring av databasdata kan dock en tidsbaserad förfallotid vara mindre än idealisk. I stället för att regelbundet rensa cacheminnet skulle det vara optimalt att endast ta bort det cachelagrade objektet när underliggande databasdata ändras. Det här idealet är möjligt med hjälp av SQL-cacheberoenden, som vi kommer att undersöka i nästa självstudie.

Lycka till med programmerandet!

Om författaren

Scott Mitchell, författare till sju ASP/ASP.NET-böcker och grundare av 4GuysFromRolla.com, har arbetat med Microsofts webbtekniker sedan 1998. Scott arbetar som oberoende konsult, tränare och författare. Hans senaste bok är Sams Teach Yourself ASP.NET 2.0 på 24 timmar. Han kan nås på mitchell@4GuysFromRolla.com.

Särskilt tack till

Den här självstudieserien granskades av många användbara granskare. Huvudgranskare för den här självstudien var Teresa Murphy och Zack Jones. Vill du granska mina kommande MSDN-artiklar? Om så är fallet, hör av dig på mitchell@4GuysFromRolla.com.