Et kig på ASP.NETs Page klasse

Tags:    asp.net
Skrevet af Bruger #4522 @ 11.03.2009

Tilstandsinformation
Vi skal nu snakke lidt om at huske.

Grundet HTTP protokollen foregår webkommunikation uden nogen form for hukommelse. Med det mener jeg at al kommunikation mellem klienten og serveren foregår uden at HTTP protokollen gemmer en form for tilstandsinformation den kan bruge til at holde styr på hver enkelt brugers tur gennem et website. For HTTP protokollen er alle HTTP forespørgelser helt unikke og uden nogen sammenhæng med nogle foregående forespørgelser. Hvis jeg sender to HTTP anmodninger til en web site - fx. kunne den første anmodning være om at se en produktside, og den anden anmodning om at lægge produktet i min indkøbskurv - aner serveren ikke at disse to anmodninger kommer fra den samme person. For alt hvad den ved kunne disse anmodninger komme fra to helt forskellige personer. Der gemmes altså ikke nogen from for tilstandsinformation i vores webkommunikation.

Dette er selvfølgelig problematisk, og i webudvikling benytter vi os derfor at forskellige tricks så vi overfor brugeren - dvs. klienten - kan lade som om at vi rent faktisk kan huske hvem han eller hun er. Et af disse trick er fx. brugen af cookies.

I mange webudviklingteknologier skal disse tricks - herunder cookies - håndteres manuelt af udvikleren. I ASP.NET derimod, er der opbygget en hel infrastruktur som sørger for at gemme og igen indlæses en sides tilstandsinformation. Dette gør det nemmere for udvikleren at opbygge den simulering af hukommelse som brugeren forventer.

En del af denne infrastruktur i ASP.NET er det der hedder view state. En sides view state er med til at opbygge en form for kontinuation i løbet af brugerens HTTP forespørgelser. For at dette "view state"-trick kan fungere skal webkontrollerne være udstyret med en runat="server" -attribut. Derudover antages det af en side "poster back" til sig selv.

Brugen af view-state foregår på følgende måde:

  1. Lige før siden renderes (dvs. dens HTML skrives til output) gemmes al sidens (og derved dens kontrollers) tilstandsinformation. Det sker ved at informationen kodes i en streng og skrives i et såkaldt gemt HTML felt.

  2. Næste gang siden postes back afkodes teksten fra det hemmelige HTML felt, og når objektinstanserne af webkotnrollerne så initialiseres på serversiden sker det med de rigtige værdier.

  3. Hvis der på klientsiden er sket en modifikation af nogle af kontrollerne opdateres tilstandsinformationen.

På denne mådes er de kontrolinstanser vi som udvikler arbejder med altid opdateret med den nyeste tilstandsinformation, og for brugeren virker det som om websiden kan huske hans tidligere indtastninger og modifikationer af siden selvom siden har været forbi webserveren.

Da en ASP.NET side næsten altid poster back til sig selv, er ASP.NET udvikling meget anderledes end andre webteknologier. En ASP.NET side indeholder én og kun én form med en runat -attribut (der kan dog godt være både en almindelig HTML form og en ASP.NET form med runat -attribut på én side), og denne form poster altid back til sig selv; derfor findes der ikke en Action attribut på en ASP.NET form.

Hændelser
Da vi kiggede på listen over metoder ovenfor, var der nogle metoder der affyrede nogle hændelser i løbet af sidens livscyklus. Disse metoder er ikke nogle du vil kalde selv, men i stedet nogle ASP.NET kalder hvorefter pågældende hændelse affyres. Du kan så vælge at reagere på nogle af disse hændelser ved at registrere en metoder der kan håndtere den/de pågældende hændelser.

I løbet af sidens livscyklus kan følgende hændelser affyres (det skal bemærkes at mens de fleste hændelser altid affyres, bliver visse hændelser kun affyret såfremt en gældende betingelse er opfyldt - fx. affyres hændelsen AbortTransaction kun såfremt en transaktion måtte afbrydes):

  • AbortTransaction: Affyres for en ASP.NET side der tager del i en (database-) transaktion som måtte afbrydes.

  • CommitTransaction: Affyres for en ASP.NET side der tager del i en (database-) transaktion som er blevet fuldført.

  • DataBinding: Affyres når metoden DataBind er blevet kaldt på siden, hvilket instruerer alle sidens kontroller om at de skal bindes til deres respektive datakilder.

  • Disposed: Affyres når sideobjektet er blevet fjernet fra hukommelsen.

  • Error: Affyres når der opstår en uhåndteret undtagelse (dvs. exception) på siden.

  • Init: Affyres når siden initialiseres.

  • InitComplete: Affyres når siden og alle dens kontroller er færdig med initialiseringsfasen.

  • Load: Affyres når siden indlæses - dette foregår efter initialiseringen.

  • LoadComplete: Affyres efter at siden er blevet indlæst.

  • PreInit: Affyres lige før sidens initialiersing.

  • PreLoad: Affyres lige føre siden indlæses.

  • PreRender: Affyres lige før sidens indhold bliver skrevet til output.

  • PreRenderComplete: Affyres når PreRender fasen er slut.

  • SaveStateComplete: Affyres når sidens view state er blevet gemt.

  • Unload: Affyres når siden er fjernet fra hukommelsen, men endnu ikke slettet.



Hver gang en side efterspørges initialiseres en instans af pågældende Page -objekt. Dette objekt, og dets kontroller, gennemgår da deres livscyklus. Denne cyklus består nogle forskellige stadier. Udvikleren kan kontrollere nogle af disse stadier gennem kode. Ovenstående liste over hændelser illustrerer de muligheder udvikleren har for at påvirke livscyklussen.

Det første der sker í denne cyklus, er at sideobjektet oprettes - ligeså med dets kontroltræ. Herefter finder køretidsmiljøet ud af hvilken type forespørgsel der er tale om. Det kan enten være:

  • En normal forespørgsel

  • En post-back

  • En post-back fra en anden side

  • Et callback



Nu kigger vi på hændelserne i kronologisk rækkefølge.

Hændelsen PreInit
Det starter med sidens PreInit hændelse. Dette starter hele livscyklussen. Om denne hændelse er følgende værd at vide:

  • Kun tilgængelig for sideobjektet

  • Posted data er tilgængelig

  • Alle kontrollerne er initialiseret med værdier fra ASPX-siden.

  • Vi kan oprette dynamiske kontroller her

  • Her (og kun her) kan vi sætte master page og tema programmatisk

  • IsCallback , IsCrossPagePotback og IsPostback er gyldige her og kan altså bruges



Hændelsen Init
Denne hændelse løber gennem kontroltræet fra bund-til-top - så når den når siden har den allerede været forbi børnekontrollerne. Dette overrasker tit mange så husk det nu!
Vi kan bide os fast på kontrollernes Init hændelse hvis vi ønsker (ved deklarativt at angive en event handler i vores ASPX kilde).
Følgende kan noteres om denne hændelse:

  • Tema og master page er nu sat, og kan ikke ændres

  • Kontrollernes ID og navnebeholder bliver sat i denne hændelse.



Hændelsen InitComplete
Som PreInit ovenfor er denne hændelse kun gyldig for sideobjektet. Denne hændelse signalerer at initialiserigsfasen nu er slut. Før denne hændelse og efter Init er sporing af view state slået til. Det betyder at hvad der fremover tilføjes/ændres til en kontrols viewstate nu bliver markeret som "dirty" og gemmes over post-backs. Det følger heraf at de properties der er sat før denne hændelse ikke gemmes som en del af kontrollens view state, hvilket også giver mening da det eneste der på nuværende tidspunkt er sat er hvad der er deklarativt beskrevet i ASPX kilden, som jo også sættes ved næste post back.

Hvis du derfor ønsker at angive en kontrols værdier programmatisk, men ikke ønsker at gemme det som en del af dens view state, skal det gøres i kontrollens Init hændelse (hvis du alligevel skal sætte værdien ved hver postback er der ingen grund at gemme den i view sate; brug generelt view state med omtanke da det virkelig kan puste siderne op med en masse unødvendig data).

Næste trin: indlæsning af view state
Der er ikke en hændelse man kan bide sig fast i hvad angår denne del af livscyklussen; hvis man har brug for kontrol over indlæsning af view state er man nødsaget til at overskrive en kontrols LoadViewState metode.

View staten indlæses når der er tale om en post back. Al sidens view state er gemt i en kodet streng i et gemt HTML felt kaldt __VIEWSTATE.

Næste trin: indlæsning af posted data
Nu kommer turen til den data der blev posted som en del af HTTP forespørgelsen. Metoden LoadPostData kaldes på de kontroller der er post data til (de rette kontroller identificeres via deres ID, såfremt der er noget post data som ikke kan matches med en kontrol gemmes pågældende data, og ASP.NET prøver senere i fasen igen at matche det).

Hændelsen PreLoad
Denne hændelse gælder kun for sideobjekter. Hændelsen markerer blot at initialiseringsfasen nu er forbi og så starter indlæsningsfasen.

Hændelsen Load
Denne hændelse starter på sideobjketet og løber rekursivt gennem alle børnekontrollerne (bemærk at det er anderledes en hvordan Init -hændelsen løber gennem træet). I denne fase er kontrollernes properties sat 100% (dvs. de er opdateret både med view state og posted data). Det er i denne fase vi typisk vil kode den logik der har med sidens opførelse at gøre.

I dette stadie kan vi bl.a. oprette dynamiske kontroller. I dette stadier prøver ASP.NET nu at matche det posted data det ikke tidligere kunne matches til en kontrol. Dette sker da det jo netop kan være at det tiloversblevne posted data stammer fra en dynamisk oprettet kontrol som ASP.NET jo ikke kendte til før end nu.

Næste trin: Kontrolhændelser
I tilfælde af post-back er det nu tid til at håndtere kontrollernes forskellige hændelser. Først affyres de hændelser der har at gøre med ændringer af en kontrols tilstand. Hvis du fx. vælger noget i en drop-down-box affyres en SelectionChanged hændelse; hvis du ændrer en tekstboks tekst affyres en TextChanged hændelse osv.

Derefter affyres den hændelse der foretog pågældende post-back. Husk at vi taler om en post-back nu, og en post-back er opstået på grund af en handling foretaget af en bruger i browseren: det mest typiske er et museklik på en knap eller et link. Fælles for dem er, at kontroller der kan forårsage et post-back implementerer interfacet IPostBackEventHandler. I tilfældet med en knap kaldes dens OnClick -metode som affyrer hændelsen Click som vi så kan håndtere i kode såfremt vi ønsker.

Hændelsen LoadComplete
Denne hændelse gælder kun for sideobjektet og markerer at nu starter renderingsfasen.

Hændelsen PreRender
Denne hændelse kaldes både på sider og kontroller, og er sidste chance for at lave opdateringer før outputtet renderes. Hændelsen affyres ført på siden og derefter rekursivt for alle kontrollerne.

Før denne hændelse vil kontroller hvis DataSourceID property er sat kalde dets DataBind metode.

Hændelsen PreRenderComplete
Denne hændelse gælder kun for sideobjektet og giver udvikleren mulighed for at vide hvornår PreRender-fasen er slut.

Hændelsen SaveStateComplete
Nu gemmes al sidens tilstandsinformation i view state. Dette sker ved at kalde SaveViewState på kontrollerne og til sidst siden selv. Metoden kan overskrives i subklaser, hvilket man kan få brug for ved custom controls o.l.

Det er også nu at control state gemmes (control state er den del af en kontrols tilstandsinformtion som er nødvendig for at kontrollen kan fungere ordentlig. Control state kan i modsætning til view state ikke slås fra og blev indført i ASP.NET 2.0, da man i ASP.NET 1.x tit oplevede problemer når man slog view state fra. Siden ASP.NET 2.0 kan man altså nu godt slå view state uden at opleve underlig opførelse fra kontrollerne. En kontrols information der har at gøre med dens funktionalitet skal altså gemmes i dens control state, hvorimod det der har med UI data at gøre skal gemmes i dens view sate).

Når denne hændelse affyres er både view og control state gemt.

Næste trin: generering af mark up
Nu genereres kontrollernes mark up. For kontroludviklere er der forskellige metoder der kan overskrives til at håndtere dette, men det er uden for denne artikels emne.

Hændelsen Unload
Denne hændelse affyres først for alle kontrollerne, og derefter for siden. Dette giver mulighed for at rydde ordentlig op - fx lukning af databaseforbindelser og lignende.

Vi har nu kigget på hele livscyklussen i ASP.NET og er snart ved vejs ende. Men nu til noget helt andet...


Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (1)

User
Bruger #16651 @ 09.10.11 21:11
Virkelig en yderst gennemarbejdet og flot artikel der gennemgår så mange detaljer! BRAVO!!
Du skal være logget ind for at skrive en kommentar.
t