V předchozím článku byly popsány principy ViewState, ukázány možnosti jeho použití a vyjmenovány jeho výhody i nevýhody. Pro řešení některých popisovaných problémů je nutné změnit výchozí konfiguraci ViewState, řešení některých problémů však vyžaduje změnu výchozí implementace ukládání. V tomto článku se proto zaměříme na popis vlastní implementace ukládání ViewState, která umožní ukládat ViewState nejen do skrytého pole v generované HTML stránce, ale také nově do databáze, adresáře v souborovém systému nebo paměťové cache serveru.

Vlastní implementace ukládání

Navržená implementace vychází z požadavků na možnost volby z více typů úložišť podle požadavků konkrétní aplikace a na snadné rozšíření o další typy úložišť. Přiložený příklad obsahuje ukládání ViewState do relační databáze, do souborového systému a paměťové cache.

Vlastní implementace ukládání je založena na překrytí metod SavePageStateToPersistenceMedium a LoadPageStateFromPersistenceMedium třídy System.Web.UI.Page. Tyto metody mají na starost (jak je to ostatně čitelné z jejich názvu), uložení a načtení hodnoty ViewState do a z konkrétního úložiště. Na následujícím obrázku je pro ilustraci uveden zjednodušený diagram tříd navrženého řešení:

Zjednodušený diagram tříd navržené implementace
Zjednodušený diagram tříd (plná velikost, cca 15 kB)

Zavedení vlastní bázové třídy stránek

Pro usnadnění implementace v aplikačních stránkách je zavedena vlastní bázové třída stránky BasePage, ve které jsou překryty zmíněné metody pro ukládání ViewState a z tohoto důvodu je vyžadováno dědění všech aplikačních stránek z této bázové stránky. Toho dosáhneme tak, že ve zdrojovém textu nově vytvořené stránky přepíšeme standardního předka System.Web.UI.Page na BasePage:

public class FirstPage : BasePage

Jazyk C# nepodporuje vícenásobnou dědičnost, takže pokud již v aplikaci bázovou třídu pro stránky používáme, je nutné implementaci vložit do ní.

Konfigurace ukládání

Načítání konfiguračních parametrů je implementováno statickou třídou Configuration. Konfigurační údaje třída načítá z aplikačního souboru web.config a umožňuje konfigurovat následující parametry:

  • Type – typ použitého úložiště, možné hodnoty jsou HiddenField (výchozí implementace), Database (uložení do databáze), File (adresář v souborovém systému) a Cache (paměťová cache)
  • ObjectExpiration – doba expirace objektů v úložišti
  • DeleteExpiredPeriod – perioda pro automatické odstraňování záznamů z úložiště
  • Directory – adresář pro ukládání záznamů, použije se pouze pokud je zvolen typ úložiště File
  • ConnectionString – připojovací řetězec k databázi, použije se pouze pokud je zvolen typ úložiště Database

Třída Configuration poskytuje statické vlastnosti pro načítání všech konfiguračních parametrů. Její metody jsou implementovány tak, aby v případě nenalezení parametru v konfiguraci vrátily výchozí hodnotu pro parametr. Například vlastnost pro zjištění jména adresáře je implementována jako:

public static string Directory
{
  get {
    try
    {
      return GetConfigValue(„Directory“);
    }
    catch
    {
      return string.Empty;
    }
  }
}

Třídy pro typy úložišť

Pro třídy implementující jednotlivé typy úložišť existuje společná abstraktní bázová třída BasePersistor. Ta deklaruje společné metody pro implementaci ukládání a také pomocné chráněné metody využívané v potomcích – pro serializaci ViewState a podobně. Tato bázová třída zavádí abstraktní metody:

  • GetViewState – pro načtení záznamu ViewState
  • SaveViewState – pro uložení záznamu
  • DeleteAppliactionViewStates – pro vymazání všech záznamů v úložišti
  • DeleteExpiredViewStates – pro vymazání expirovaných záznamů z úložiště

S výjimkou výchozího typu ukládání (HiddenField) je pro každý typ úložiště vytvořena třída zděděná z BasePersistor. Pro ukládání do databáze je implementována třída DatabasePersistor, pro ukládání do souborového systému slouží FilePersistor a pro paměťovou cache CachePersistor. Pro výchozí typ HiddenField je v překrytých metodách stránky použita funkčnost metod předka, není tedy nutné pro tento typ implementovat vlastní třídu.

Jedním z cílů řešení je možnost snadného rozšiřování o další typy úložišť. Vytvoření dalšího typu úložiště lze provést vytvořením potomka třídy BasePersistor a implementací výše uvedených abstraktních metod. Pokud nová třída bude pro svoji funkci vyžadovat vlastní konfigurační údaje, je nutné je přidat jako parametry do souboru web.config a přidat pro ně odpovídající statické vlastnosti ve třídě Configuration.

Identifikace záznamu v úložišti

Pro jednoznačnou identifikaci záznamu je použita proměnná typu GUID, která se ukládá do generované stránky prostřednictvím skrytého pole s názvem „__VIEWSTATE_KEY“. Výchozí implementace ukládá hodnoty do skrytého pole „__VIEWSTATE“, přičemž přítomnost tohoto pole ve stránce je pro správnou funkci vyžadována. Toto pole v případě použití serverových typů úložišť ponecháváme prázdné, přičemž v generování pole existuje drobný rozdíl mezi implementací v .NET Frameworku verze 1.0 a 1.1, kdy ve verzi 1.0 není toto pole automaticky generováno do stránky, zatímco ve verzi 1.1 ano. Kvůli vyžadované přítomnosti tohoto pole je pro aplikace pracující s .NET Frameworkem verze 1.0 nutné toto pole vygenerovat v metodě SavePageStateToPersistenceMediumprogramově:

if (Environment.Version.Major == 1 && Environment.Version.Minor == 0)
{
  RegisterHiddenField(„__VIEWSTATE“, „“);
}
RegisterHiddenField(„__VIEWSTATE_KEY“, viewStateID.ToString());

Překrytí metod pro uložení a načtení ViewState

Máme již připraveny potřebné třídy, které umožní překrytí implementace metod SavePageStateToPersistenceMedium a LoadPageStateFromPersistenceMedium. Základní funkce algoritmu obou metod je stejná:

  1. pomocí třídy Configuration je zjištěn typ úložiště

    EViewStateStore configuredStore = Configuration.ViewStateStore;

  2. pokud se jedná o výchozí implementaci, tak je pouze zavolána metoda bázové třídy:

    base.SavePageStateToPersistenceMedium(viewStateBag);
    respektive „return base.LoadPageStateFromPersistenceMedium();“

  3. pokud jsou použita serverová úložiště, je podle nakonfigurovaného typu vytvořena instance odpovídající třídy a případně jsou načteny vyžadované konfigurační parametry pro tento typ (příklad pro adresář v souborovém systémy):

    string configuredDirectory = Configuration.Directory;
    usedPersistor=new FilePersistor(configuredDirectory, configuredExpiration);

Uložení záznamu

Metoda pro uložení zajistí vygenerování nebo načtení již existujícího identifikátoru záznamu…

Guid viewStateID = Guid.Empty;
if (Request.Form[„__VIEWSTATE_KEY“] != null)
{
  viewStateID = new Guid(Request.Form[„__VIEWSTATE_KEY“]);
}
else
{
  viewStateID = Guid.NewGuid();
}

…a uložení záznamu do úložiště:

usedPersistor.SaveViewState(
  viewStateID,
  viewStateBag);

Následně metoda zaregistruje skryté pole s identifikátorem do stránky, případně ještě zajistí výše zmiňované generování povinného pole __VIEWSTATE:

if (Environment.Version.Major == 1 && Environment.Version.Minor == 0)
{
  RegisterHiddenField(„__VIEWSTATE“, „“);
}
RegisterHiddenField(„__VIEWSTATE_KEY“, viewStateID.ToString());

Načtení záznamu

Metoda pro načtení zjistí hodnotu identifikátoru záznamu…

Guid viewStateID = new Guid(Request.Form[„__VIEWSTATE_KEY“]);

…a poté načte záznam a vrátí ho jako návratovou hodnotu metody:

return usedPersistor.GetViewState(viewStateID);

Odstranění záznamů z úložiště

Pro kompletní implementaci ukládání záznamů ViewState do úložišť na serveru schází pouze zajištění odstranění již neplatných záznamů z úložiště. Za tuto činnost odpovídá třída StoreCleaner. Třída implementuje dva způsoby mazání záznamů – výmaz všech záznamů a výmaz záznamů s dobou posledního přístupu vyšší než nastavená hodnota životnosti záznamu. Metoda pro vymazání všech záznamů je použitelná pouze v době, kdy je zajištěno, že žádný záznam z úložiště není a nebude využíván, tedy typicky při startu nebo ukončení aplikace (metoda Application_Start nebo Application_End v souboru global.asax).

Druhá metoda vyžaduje periodické spouštění, proto třída StoreCleaner implementuje časovač, který v nastavené periodě vymaže všechny expirované záznamy z úložiště. Tento časovač se spouští pomocí metody StartAutomaticExpiredDeletion a zastavuje metodou StopAutomaticExpiredDeletion. Místem pro start časovače je logicky také doba startu aplikace, konkrétně například metoda Application_Start. Protože je však nutné zajistit automatickou kontrolu nastavené doby životnosti objektu vzhledem k délce trvání Session (hodnota životnosti objektu nemůže být nižší než hodnota doby životnosti Session), je nutné start časovače umístit do Session_Start a programově zajistit, aby bylo možné časovač spustit maximálně jednou (toto zajišťuje třída StoreCleaner pomocí principu vycházejícího z návrhového vzoru Singleton).

Konkrétní implementace v metodách global.asax je tedy následující:

protected void Application_Start(Object sender, EventArgs e)
{
  StoreCleaner.DeleteApplicationViewStates();
}
protected void Session_Start(Object sender, EventArgs e)
{
  StoreCleaner.StartAutomaticExpiredDeletion();
}
protected void Application_End(Object sender, EventArgs e)
{
  StoreCleaner.StopAutomaticExpiredDeletion();
  StoreCleaner.DeleteApplicationViewStates();
}

K dispozici je vám rovněž příklad v podobě archivu kompletní sady zdrojových kódů, které můžete využít ve vlastních aplikacích.

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