Uchování ViewState mimo asp.net stránku
O tom, že protokol http je bezstavový je jeho vlastnost se kterou se musí vypořádat nejeden vývojář webových aplikací. Microsoft přišel v asp.net s tím, používat pro uchování stavu požadavků mezi jednotlivými requesty klienta, ViewState. ViewState je ve výchozím nastavení webového projektu povolen a jeho uchování se děje na základě vloženého input prvku typu hidden do renderované HTML stránky.
Tento způsob uchování ViewState není jediný možný a již ve verzi asp.net 1.x byla možnost jej za pomoci přepsání dvou metod změnit. ASP.NET 2.0 však jde ještě dál a poskytuje přímo abstraktní třídu PageStatePersister, ve které stačí přepsat dvě metody pro uložení a načtení ViewState a ControlState a stav uložit dle návrhu aplikace. V tomto článečku ukáži, jak takový stav uložit do databáze.
Pro napsání tohoto příspěvku, s poměrně triviálním kódem, jsem se rozhodl z toho důvodu, že se opět rozbouřily vody kolem asp.net a jeho renderování výstupu HTML na klienta. Nejsem si jist, zda to rozpoutal Borber s uveřejněním své bakalářské práce, na kterou reagoval Jakub Vrána. Nebo to bylo až poté, co se DGX pozastavil nad výroky Marcuse, a kdy jsem se do debaty vložil také osobně?
Hnedka na úvod musím říci, že ViewState využívám, snažím se však o to, aby byl využívám účelně a jeho velikost zbytečně nebobtnala. Proto je vždy důležité zvolit správné UI kontroly a v případech kdy to jde, zakázat ViewState zcela. Jelikož píši z převážné většiny intranetové aplikace, nebráním se ViewState využívat. Avšak přišlo mi velice neefektivní jej přenášet při každém požadavku na klienta a zase zpět. Také proto jsem zvolil uchování stavu na serveru a to přímo v databázi - přeci jen v aplikacích je těch dotazů do db několik a jeden dotaz navíc je téměř zanedbatelný.
Vlastní implementace
Důležité je říci stránce, aby používala náš vlastní persister. V případě, že používáte vlastní poděděnou třídu Page jako výchozí pro všechny ostatní, což z vlastní zkušenosti doporučuji, je postačující přepsat property PageStatePersister
protected override PageStatePersister PageStatePersister { get { return new DbPagePersister(Page, "connectionStringKey");; }}
Jestliže nemáte stránky navrženy výše uvedeným způsobem, nemusíte ještě zoufat. Můžete využít PageAdapter, a obdobně zde přepsat metodu GetStatePersister(), následně pomocí souboru .browser tento adaptér pro generování stránek přiřadit.
Dříve však než můžeme zaregistrovat vlastní persister, musíme si jej vytvořit. Vytvoříme tedy novou třídu a podědíme ji od abstraktní třídy PageStatePersister. Zde přepíšeme dvě metody pro uložení Save() a načtení Load() stavu. Předpokládám, že každý umí uložit data do databáze a tak tento krok přeskočím, nicméně je v přiložené ukázce implementován. Co je však důležité, jakým způsobem bude identifikována stránka, abychom mohli načíst její stav zpět. To je zajištěno pomocí registrace hidden pole a přiřazení unikátního řetězce Guid. Tento je jako jediný odeslán na výstup stránky a při zobrazení zdrojového kódu jej zde naleznete.
Pod čarou
Pokud se podíváte na výsledný zdrojový kód, možná vás překvapí, že se zde vyskytuje hidden pole nazvané __VIEWSTATE avšak je prázdné. Toto pole se mi však nepodařilo využít pro uložení jednoznačného identifikátoru, stejně tak se mi nepodařilo pole odstranit z finálního výstupu. Tím ovšem neříkám, že to ve výsledku nelze, ale o tom až příště.
Zdroje
Ukázka ke stažení.
5 Comments
rarouš said
Bezva k napsání podobného článku se přemlouvám už dlouho, ale nevyzbyl mi čas. tak teĎ už nemusím :D
Jakub Müller said
<p>Koukal jsem se na ten kód, a není mi jasná jedna věc - jak se odstraňují záznamy se ViewState od stránek, na kterých už nevzniknul PostBack?</p>
<p>Jestli se nepletu, tak se tímto způsobem budou v DB množit záznamy s uloženým ViewState stránek, které už nikdy nebudou potřeba.</p>
said
<p>2Jakub: Zde záleží na vytíženosti a zaměření stránek. V tabulce je uchována informace o vytvoření stavu, záleží pak na konkrétní situaci jak se bude odmazávání "neplatných" stavů řešit. </p>
<p>Pro intranetový projekt, kde jsem toto použil (ještě verze fw 1.0) se odmazávaly stavy vždy přes noc. </p>
<p>Toto může být skutečně individuální a určitě ne všude je vhodné použít ukládání stavu do DB. Je klidně možné evidovat se stavem relace i SessionId a při ukončení relace celou historii vymazat. Těch variant je skutečně mnoho.</p>
Jakub Müller said
<p>Diky, to jsem si myslel. Zajímalo mě, jestli to náhodou neřeší nějaká vychytávka "vedle", kterou neznám.</p>
tz said
Ukázka nejde stáhnout (Server Error in '/' Application).