V ASP.NET máme k dispozici objekt Application pro sdílení položek mezi různými session téže aplikace. Všichni klienti naší aplikace tak mohou pracovat s tímtéž údajem, aniž bychom potřebovali takový údaj ukládat například do databáze. V našem příkladu budeme v Application držet údaje žertovné ankety.

Cílem naší aplikace bude vyrobit měřič aktuální nálady – tedy anketu s jednou otázkou a deseti možnými odpověďmi. Ukládat budeme datum hodnocení, aktuální průměrné hodnocení a počet hodnocení. Každému uživateli umožníme hlasovat 1x za hodinu – zopakujeme si práci s cookies. Jakmile se datum přehoupne do následujícího dne, zahájíme anketu znovu a dosavadní výsledky prostě vynulujeme. Aktuální výsledek zobrazíme formou obrázku a v procentech. Prohlédněte si ukázku (zdrojový kód).

Objekt Application

Objekt Application zajišťuje globální sdílení informací mezi různými sessions a požadavky klientů v aplikaci. Kdokoli vyvolá nějaký požadavek, může tak obdržet vždy aktuální hodnotu nějaké položky, a tato je pro všechny požadavky nezávisle na session jediná. Tento tzv. Application state je tvořen souhrnem všech souborů, stránek, handlerů, modulů nebo prostě kódu v rozsahu virtuální aplikace (a jejích podadresářů) na jednom webovém serveru.

Application state jako jedinečná instance třídy HttpApplicationState je vytvořen při prvním požadavku na danou URL virtuální aplikace a v aplikaci je pak dostupný jako vestavěný (intrinsic) objekt Application. Každá virtuální aplikace má svůj vlastní Application state, mezi aplikacemi tedy není možné informace v objektu Application sdílet.

Je dobré vědět, že při restartu aplikace, restartu služeb IIS nebo recyklaci procesu zpracovávajícího požadavky klientů (tzv. worker process realizovaný spuštěným aspnet_wp.exe) dojde ke ztrátě (resetu) údajů Application, Application state se ztratí. Chceme-li využívat aplikaci, která je závislá na Application state, je třeba vhodně nastavit vlastnosti virtuální aplikace na serveru. Verze IIS 6 již podporuje oddělené Application pool s různým nastavením pro různé aplikace, pohodlně si tak pro takovou aplikaci vytvoříme Application pool, který bude vyhrazený pro naši aplikaci a nebude samočinně recyklovat a restartovat worker proces. Závěrem je třeba upozornit, že Application state není sdílený mezi stroji webové farmy ani mezi procesy webové zahrady (více procesů běžících obyčejně na víceprocesorovém stroji), tento problém řeší nasazení Microsoft Application Center.

Náladoměr

Nejprve si popíšeme formulář, ve kterém jsou hlasovací prvky i prvky zobrazující aktuální stav:

<h1>Náladoměr – jaká je dnes nálada?</h1>
<div>
  <asp:Image Id=“imgSpirit“ Visible=“false“ Width=“150px“ Height=“150px“ BorderWidth=“0px“ AlternateText=“Dnešní nálada“ ToolTip=“Dnešní nálada“ RunAt=“server“ />
  <asp:Label Id=“lblSpirit“ Text=“Dnešní náladu ještě nikdo nehodnotil…“ RunAt=“server“ />
</div>
<form RunAt=“server“>
  <asp:Panel Id=“pnlButtons“ RunAt=“server“>
    Jakou máš náladu?
    <br /><br />
    <asp:ImageButton AlternateText=“1″ ImageUrl=“Images/Smile_0.gif“ CommandArgument=“0″ Style=“cursor:pointer;“ CommandName=“spirit“ OnCommand=“SetSpirit“ Width=“30px“ Height=“30px“ BorderWidth=“0px“ RunAt=“server“ />
    <asp:ImageButton AlternateText=“2″ ImageUrl=“Images/Smile_1.gif“ CommandArgument=“1″ Style=“cursor:pointer;“ CommandName=“spirit“ OnCommand=“SetSpirit“ Width=“30px“ Height=“30px“ BorderWidth=“0px“ RunAt=“server“ />
    <asp:ImageButton AlternateText=“3″ ImageUrl=“Images/Smile_2.gif“ CommandArgument=“2″ Style=“cursor:pointer;“ CommandName=“spirit“ OnCommand=“SetSpirit“ Width=“30px“ Height=“30px“ BorderWidth=“0px“ RunAt=“server“ />
    <asp:ImageButton AlternateText=“4″ ImageUrl=“Images/Smile_3.gif“ CommandArgument=“3″ Style=“cursor:pointer;“ CommandName=“spirit“ OnCommand=“SetSpirit“ Width=“30px“ Height=“30px“ BorderWidth=“0px“ RunAt=“server“ />
    <asp:ImageButton AlternateText=“5″ ImageUrl=“Images/Smile_4.gif“ CommandArgument=“4″ Style=“cursor:pointer;“ CommandName=“spirit“ OnCommand=“SetSpirit“ Width=“30px“ Height=“30px“ BorderWidth=“0px“ RunAt=“server“ />
    <asp:ImageButton AlternateText=“6″ ImageUrl=“Images/Smile_5.gif“ CommandArgument=“5″ Style=“cursor:pointer;“ CommandName=“spirit“ OnCommand=“SetSpirit“ Width=“30px“ Height=“30px“ BorderWidth=“0px“ RunAt=“server“ />
    <asp:ImageButton AlternateText=“7″ ImageUrl=“Images/Smile_6.gif“ CommandArgument=“6″ Style=“cursor:pointer;“ CommandName=“spirit“ OnCommand=“SetSpirit“ Width=“30px“ Height=“30px“ BorderWidth=“0px“ RunAt=“server“ />
    <asp:ImageButton AlternateText=“8″ ImageUrl=“Images/Smile_7.gif“ CommandArgument=“7″ Style=“cursor:pointer;“ CommandName=“spirit“ OnCommand=“SetSpirit“ Width=“30px“ Height=“30px“ BorderWidth=“0px“ RunAt=“server“ />
    <asp:ImageButton AlternateText=“9″ ImageUrl=“Images/Smile_8.gif“ CommandArgument=“8″ Style=“cursor:pointer;“ CommandName=“spirit“ OnCommand=“SetSpirit“ Width=“30px“ Height=“30px“ BorderWidth=“0px“ RunAt=“server“ />
    <asp:ImageButton AlternateText=“10″ ImageUrl=“Images/Smile_9.gif“ CommandArgument=“9″ Style=“cursor:pointer;“ CommandName=“spirit“ OnCommand=“SetSpirit“ Width=“30px“ Height=“30px“ BorderWidth=“0px“ RunAt=“server“ />
  </asp:Panel>
</form>
<asp:Label Id=“lblVoted“ Visible=“false“ Text=“Díky za hodnocení, znovu hodnotit budeš moci v následující hodině :)“ RunAt=“server“ />“

Ve stránce máme Image ukazující aktuální výsledek hlasování. Pokud ještě nikdo nehlasoval, je zneviditelněn a namísto něho je viditelný Label informující o tom, že ještě nebylo hlasováno. Vlastnosti prvků jsou nastavovány programově (viz níže). Formulář s Panelem obsahuje hlasovací ImageButtony (obrázková tlačítka) a Label zobrazující informaci o již proběhlém hlasování. Před hlasováním je viditelný obsah panelu, po hlasování je panel skryt a naopak je zviditelněna zpráva, že již bylo hlasováno.

O použití cookies v ASP.NET jsme již psali, dále použitý objekt Application nabízí několik vlastností a metod – v naší aplikaci nám však postačí pouze nastavit a přečíst hodnotu položky (Item, v C# je indexerem instance třídy, čili zapisujeme pouze index do hranatých závorek).

<%@ Page Language=“C#“ Trace=“False“ EnableSessionState=“False“ Debug=“False“ %>
<script language=“C#“ runat=“server“>
void SetSpirit(object sender, CommandEventArgs e)
{
  int ActualSpirit = Int32.Parse((String) e.CommandArgument);
  if (ActualSpirit >= 0 && ActualSpirit < 10)
  {
    Double meterSpirit = 0;
    int meterCount = 0;
    DateTime meterDate = DateTime.Today.AddDays(-1);
    if (Application[„MeterDate“] != null)
      meterDate = (DateTime) Application[„MeterDate“];
    if (!(meterDate.Date < DateTime.Today))
    {
      if (Application[„MeterSpirit“] != null)
        meterSpirit = (Double) Application[„MeterSpirit“];
      if (Application[„MeterCount“] != null)
        meterCount = (int) Application[„MeterCount“];
    }
    Application[„MeterSpirit“] = (meterSpirit*meterCount+(ActualSpirit *11.1))/(meterCount+1);
    Application[„MeterCount“] = meterCount+1;
    Application[„MeterDate“] = DateTime.Today;
    HttpCookie myCookie = new HttpCookie(„SpiritM“);
    myCookie.Value = „1“;
    myCookie.Expires = DateTime.Now.AddHours(1);
    Response.Cookies.Add(myCookie);
    Response.Redirect(„Default.aspx“,true);
  }
}
void Page_Load(object sender, System.EventArgs e)
{
  Double meterSpirit = 0;
  int meterCount = 0;
  DateTime meterDate = DateTime.Today.AddDays(-1);
  if (Application[„MeterSpirit“] != null)
    meterSpirit = (Double) Application[„MeterSpirit“];
  if (Application[„MeterCount“] != null)
    meterCount = (int) Application[„MeterCount“];
  if (Application[„MeterDate“] != null)
    meterDate = (DateTime) Application[„MeterDate“];
  if (meterDate.Date == DateTime.Today.Date)
  {
    lblSpirit.Visible = false;
    imgSpirit.Visible = true;
    imgSpirit.ImageUrl = „Images/Smile_“ + ((Math.Round(meterSpirit/11.1))).ToString() + „.gif“;
    imgSpirit.AlternateText = „Dnešní nálada: “ + Math.Round(meterSpirit).ToString() + „%, hodnoceno: “ + meterCount.ToString() + “ x“;
    imgSpirit.ToolTip = imgSpirit.AlternateText;
  }
  else
  {
    lblSpirit.Visible = true;
    imgSpirit.Visible = false;
  }
  HttpCookie myCookie = new HttpCookie(„ckStatus“);
  myCookie = Request.Cookies[„SpiritM“];
  if (myCookie != null && imgSpirit.Visible)
  {
    pnlButtons.Visible = false;
    lblVoted.Visible = true;
  }
  else
  {
    pnlButtons.Visible = true;
    lblVoted.Visible = false;
  }
}
</script>

Nejprve popíšeme obsluhu Page_Load. Připravíme si proměnné pro počet měření a aktuální průměrnou hodnotu měření (v procentech) a pokusíme se obnovit jejich stav z Application. Pokud již nějaké měření proběhlo, bude nastaveno i datum posledního měření – porovnáme, je-li shodné s datem dnešního dne. Pokud ano, znamená to, že hodnoty měření jsou aktuální a zobrazíme tedy aktuální stav. Jméno souboru obrázku ImageUrl stanovíme výpočtem z aktuálního stavu měření, který vydělíme konstantou 11.1 a zaokrouhlíme. Takto totiž získáme průměrnou hodnotu odpovídající hodnocením z obrázkových tlačítek – těch je celkem 10 a nabývají hodnot 0 až 9. Poslední, nejvyšší, hodnotu bereme jako 100 %, pokud tedy vypočteme 100/9, dostaneme konstantu 11.1, kterou je třeba násobit každou úroveň hodnocení pro získání odpovídající hodnoty v procentech. 9*11.1 je sice 99.9 %, ale protože používáme zaokrouhlení, je výsledkem opět správně 100 %. Do alternativního popisku obrázku AlternateText ještě nastavíme text se zakrouhleným hodnocením v procentech a počet měření. Tentýž text ještě nastavíme do vlastnosti ToolTip (bude renderován jako atribut title), aby korektnosti kódu odesílaného klientovi bylo učiněno zadost.

Pokud se nezdaří obnovit hodnoty z Application, nebo datum neodpovídá dnešnímu datu, obrázek hodnocení je zneviditelněn (nebude renderován) nastavením vlastnosti Visible na false. Naopak je zviditelněn text informující, že měření ještě neproběhlo.

Dále otestujeme, zda existuje cookie informující o již proběhlém hlasování. Ohledně práce s cookies odkazuji na výše zmíněný článek. Pokud je cookie nastavena a obrázek hodnocení není zneviditelněný, odepřeme hlasování. Panel s hlasovacími prvky skryjeme a zviditelníme hlášení, že uživatel již hlasoval a bude moci znovu hlasovat později. Pokud cookie nastavena není, nebo ještě vůbec nikdo nehlasoval, umožníme uživateli hlasovat – panel hlasovacích tlačítek necháme viditelný a naopak hlášení o hlasování bude skryto.

Dále si popíšeme metodu SetSpirit, která zajišťuje zpracování hlasování při kliknutí na obrázkové tlačítko. Metoda přijímá jako parametr CommandEventArgs, z něj pak uvnitř metody jsme schopni rozlišit jaká hodnota byla při klepnutí na tlačítko předána. Hodnotu získáme z vlastnosti CommandArgument, ještě bychom mohli rozlišovat i jaký povel CommandName byl požadován, naše aplikace si však vystačí s jedinou funkcí pro všechna tlačítka, a tak tuto vlastnost nerozlišujeme. Připravíme proměnné pro stav měření, počet měření a datum měření – tu nastavíme na včerejší datum. Dále se pokusíme obnovit datum měření z Application, poté datum měření porovnáme- jsou-li údaje o měření z dnešního dne, obnovíme hodnoty měření z Application. Jinak budeme počítat s nulovou hodnotou měření a nulovým počtem měření. Přepočítené hodnoty poté uložíme do Application včetně dnešního data měření. Princip výpočtu je obdobný jako již popsaný výpočet cesty obrázku – v principu jde o práci s práci s procenty a matematickou trojčlenku. Nakonec nastavíme cookie informující o proběhlém hlasování, dobu expirace nastavíme na 1 hodinu, po dobu jedné hodiny tak bude mít uživatel odepřeno hlasování. Nakonec provedeme přesměrování na výchozí stránku aplikace, čímž se zbavíme údajů, které by jinak mohl klient opětně odeslat do aplikace prostým obnovením stránky (stiskem F5 ap.). Zde by bylo možné ještě přidat nějaký parametr, který by znepřístupnil hlasovací prvky i u klientů, kteří nepodporují cookies – pro přehlednost jsem to ale vynechal.

Aplikace je míněna jako žertovná ukázka možného využití Application, obdobně bychom mohli vytvořit například jednoduchý chat, který by uchovával třeba posledních 10 zpráv. Zajímavé je také použití pro aplikace, které pracují s HW serveru – například ovládají nějaké zařízení připojené k portům. Toto si ukážeme v některém z příštích článků. Ověřím, zda hodnota je v patřičném rozsahu (zda nám někdo do aplikace nepodvrhuje nesmysly, které by znepřesnily měření nebo dokonce vedly nesmyslným vysokým nebo záporným výsledkům).

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