Welcome to MSDN Blogs Sign in | Join | Help

Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

- Og denne gang er der noget på højkant!

Vi må ta' et lille kig på dynamisk indsatte UserControl's, så lad os få kridtet banen op :o)

I forbindelse med en lille løsning du sidder og hygger dig med, har du lavet en mindre UserControl:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ExtraOptions.ascx.cs" Inherits="ExtraOptions" %> <!-- Diverse kontroller her -->

Med nedenstående kode:

public partial class ExtraOptions : System.Web.UI.UserControl { protected void Page_Load(object sender, EventArgs e) { // En masse kode der læser data, manipulerer kontroltræer og ændrer verden. } public void SetDefaultChoice(string choiceText) { // En eller anden implementering } }

Og du skynder dig at gå igang med at bruge den.

Nu hedder kontrollen jo ExtraOptions og skal være en tilføjelse på forskellige sider, i forskellige situationer, og du vælger en løsning, hvor den indsættes dynamisk, baseret på en parameter i QueryString, og laver på én af dine sider noget i denne stil:

public bool ShowExtraOptions { get { bool extraOptions = false; bool.TryParse(Request.QueryString["ExtraOptions"], out extraOptions); return extraOptions; } } protected void Page_Load(object sender, EventArgs e) { if (ShowExtraOptions) { // du loader kontrollen Control extraOptions = LoadControl("ExtraOptions.ascx"); // typecaster og kalder noget kontrolspecifikt ((ExtraOptions)extraOptions).SetDefaultChoice("Choice2"); // og tilføjen kontrollen til en placeholder UxDynamicControlContainer.Controls.Add(extraOptions); } }

Du fortsætter nu med andre ting, og er slet ikke opmærksom på at du netop har skrevet et stykke risikabelt kode, og samtidig påpeget en væsentlig uhendsigtmæssighed i ASP.NET!

Nu er spørgsmålet jo så:

Hvad er det du har gjort der ikke er godt nok, og hvad er det for en uhendsigtsmæssighed jeg snakker om?

Og som skrevet er der er denne gang en præmie at vinde, nemlig sådan en kop her:

"Uhh buhh - en tiger - den har vi da set før!!!"

Jahh - det har i jo sikkert, men den bliver naturligvis fyldt med nogle kaffebønner der er lidt ud over det sædvanlige, så nu har du muligheden for at smage kaffen du aldrig ville købe selv ;o)


Mvh

Published Sunday, June 24, 2007 11:42 AM by jepper
Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Monday, June 25, 2007 3:14 AM by Hartvig

Den sidste gætter jeg på er at ved først at loade din kontrol i page_load event'en, vil eventuelle kontroller i din dynamiske kontrol ikke kalde events før det. Dvs. bl.a. onInit event'en på de dynamiske kontroller vil ikke blive kaldt.

For at opdage den risikable del, skal jeg have noget kaffe - det er mandag morgen, dammit :o)

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Monday, June 25, 2007 3:56 AM by jepper

Hej Niels :o)

Mandag morgen - ja det skal jeg da godt nok lige love for! 03:14?!?... Det må man vist kalde meget tidligt mandag morgen :o)

Jo - init fyrer fint i kontrollen, også når den indsættes dynamisk på den måde (og på det tidspunkt).

Kan du ryste et bud til ud ad ærmet?

Mvh

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Monday, June 25, 2007 7:51 PM by Jesper Blad Jensen

Jeg vil mene at:

Da du indlæser din kontrol i load, læser du den EFTER LoadViewState. Da du gør det vil den ikke beholde state - altså din kontrol bliver nød til at loades hver gang, da dens state ikke bliver husket.

Ideen er at hvis kontrollen bliver gemt i SaveViewState, men ikke findes i heriakiet, i LoadViewState, så får den aldrig sin state bag.

Det giver det problem at vi ikke kan bruge ViewState i vores kontrol - altså man kan sige at den bliver "glemt".

Det man skal gøre at at indsætte din kontrol FØR loadviewstate, og det kan man gøre ved at være med på en lytter ved Init eventet på Page, og ligge sin kode der. Dog skal man lige bemærke at det eneste man skal ligge her er der hvor man laver kontrollen. Hvis du kalder f.eks. SetDefaultChoice i Init, vil den ikke have ViewState da den ikke er loadet. Og det kan give mærkelige hældelser hvis denne metode regner med viewstate!

Derfor:

Lav dine kontroller i Init, og sæt deres Properties og state and whatever i Page_Load som du plejer.

Jeg håber det var det du mente :)

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Tuesday, June 26, 2007 5:35 AM by jepper

Hej Jesper :o)

Nu er det jo altid spændende når ViewState kommer på banen ;o) - men det er dog ikke det der er balladen her. ViewState er dog et godt tema til den næste quiz ;o)

Som udgangspunkt bliver jeg nødt til at loade mine dynamiske kontroller hver gang, og jeg kan sagtens benytte ViewState i ovenstående eksempel uden at løbe ind i problemer.

Faktisk kan man sige - i og med jeg har en initialisering af kontrollen (underforstået at det er det der ligger i SetDefaultChoice(...) ved hvert postback - at det ville være uhendsigtsmæssigt, hvis properties jeg satte i SetDefaultChoice blev serialiseret til ViewState.

Næh... Det er noget andet jeg fisker efter her, og jeg har nok - i min iver efter at sørge for at svaret ikke "bare" kan søges frem med et par nøgleord - fået formuleret den en smule for indirekte, så derfor:

Hint nummer 1)

Nedenstående konstruktion:
Control extraOptions = LoadControl("ExtraOptions.ascx");
((ExtraOptions)extraOptions).SetDefaultChoice("Choice2");

Ser som udgangspunkt meget tilforladelig ud... Et "hårdt" typecast skærer self. altid en smule i øjnene, men når nu linien inden har sørget for at load'e en specifik kontrol, skulle der jo ikke være noget i vejen for at typecaste returværdien til denne type... Eller hva?!?

Svaret er faktisk "eller hva"... LoadControl vil IKKE altid returnere en instans af den UserControl der er givet en sti til - og det er den situation jeg er ude efter.

Så først og fremmest - koden der er utilstrækkelig, er denne:
((ExtraOptions)extraOptions).SetDefaultChoice("Choice2");

Og et hint til uhensigtsmæssigheden i ASP.NET er, at det rent faktisk er noget jeg kan gøre deklarativt i selve UserControl'en der vil medføre at LoadControl IKKE returnerer en ExtraOptions instans, men noget helt andet, og dermed knække min kode.

Jeg håber det kan hjælpe lidt på vej, og ellers vil der komme et hint til senere ;o)

Blive endelig hængende - svaret er bestemt værd at være bekendt med.

Mvh

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Tuesday, June 26, 2007 8:23 AM by Jakob Andersen

Nu er du ude på lidt af en fiskeekspedition så ved ikke om mit svar er det du leder efter men det var bare det første der sprang frem i mit hovede før jeg læste kommentarerne, og så må jeg indrømme du forvirrede mig pænt meget med dine hints :D

Jeg ser problemet som at der kunne jo være at systemet skulle optimeres senere og en udvikler ganske fornuftigt ville sætte caching på User Kontrollen vha. OutputCache direktivet.

I det tilfælde vil LoadControl returnere en instans af typen PartialCachingControl istedet for kontrollen. Og for rent faktisk at få fat i kontrollen skal vi tilgå egenskaben CachedControl på denne instans og caste denne til vores ExtraOptions type.

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Tuesday, June 26, 2007 8:27 AM by Johannes Hansen

Hejsa.

Her er så mit bud på hvad du vil frem til. ;)

LoadControl returnerer den genererede type (genereret fra ascx filen) som nedarver fra den type som er specificeret i "Inherits" i "<%@ Control..." tagget. Normalt vil ascx kontrollen nedarve fra den klasse som er defineret i code-behind filen men det kan dog være en anden klasse. Men hvis man udelader at specificere "Inherits" er det dog "UserControl" som der bliver nedarvet fra. Det er derfor muligt at ExtraOptions.ascx ikke nedarver fra typen ExtraOptions men fra noget andet hvorved det direkte typecast til ExtraOptions vil fejle. Jeg mener ikke at "<tagMappings />" elementet i web.config har noget at sige i denne situation men det kunne da være rart at få det klargjort hvorvidt det er tilfældet eller ej.

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Tuesday, June 26, 2007 8:54 AM by Johannes Hansen

Som opfølgning på kode eksemplet kunne Default.aspx code-behind filen se ud som følgende:

private const bool DefaultValue_ShowExtraOptions = false;

public bool ShowExtraOptions
{
 get
 {
   bool value;
   // Ja ja, ikke en vigtig ændring... Det er bare så man også kan have en "true" default værdi hvis man har lyst.

   if (!bool.TryParse(Request.QueryString["ExtraOptions"], out value))
     value = DefaultValue_ShowExtraOptions; 
   return value;
 }
}

protected void Page_Load(object sender, EventArgs e)
{
 if (ShowExtraOptions)
 {
   // du loader kontrollen
   Control control = LoadControl("ExtraOptions.ascx");
   // konverterer den til en typestærk kontrol med "as ExtraOptions"
   ExtraOptions extraOptions = control as ExtraOptions;
   // checker om "as ExtraOptions" resulterede i en instans og kalder noget ExtraOptions specifikt
   if (extraOptions != null)
     extraOptions.SetDefaultChoice("Choice2");
   // og sidst men ikke mindst tilføjes kontrollen til en placeholder
   // bemærk at vi bruger "control" og ikke "extraOptions" instansen
   // da "extraOptions" kan være null selvom "control" ikke er det.
   Page.Controls.Add(control);
 }
}

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Tuesday, June 26, 2007 9:41 AM by jta

Nå, men jeg kan da lige knytte lidt til Johannes svar. Hvis vi skal sikre os at det er den rigtige type vi får indlæst og vi stadig skal tage højde for cachede kontroller er det oplagt at skrive en lille generisk udgave af LoadControl til at hjælpe os, et forslag kunne være den her:

protected T LoadControl<T>(string virtualPath, ControlCollection addTo) where T : Control{
       Control ctrl = LoadControl(virtualPath);
       if(ctrl != null){
           addTo.Add(ctrl);
       }

       if(ctrl is T){
           return ctrl as T;
       }else if(ctrl is PartialCachingControl && ((PartialCachingControl)ctrl).CachedControl is T){
           return ((PartialCachingControl)ctrl).CachedControl as T;
       }

       return null;
}

Med den kan vi få tilføjet vores kontrol og få returneret den rigtige type uafhængigt af caching. Det er værd at huske at CachedControl egenskaben på PartialCachingControl er null medmindre kontrollen er tilføjet til kontroltræet. Man kunne også vælge at lade ovenstående metode kaste en exception hvis det ikke lykkedes at caste istedet for at returnere null

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Tuesday, June 26, 2007 9:55 AM by jta

Og alt kode man skriver i kommentarer kommer så til at se knap så godt ud :(

Nå, men jeg begynder da efterhånden at glæde mig til at Jeppe får løftet sløret :)

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Tuesday, June 26, 2007 10:26 AM by jepper

Damn - nu sker der ting og sager herude :o)

Jeg var ellers i fuld gang med at skrive til jer, men med de sidste kommentarer er det blevet outdatet :o)

Jakob - du har fuldstændig ret i at det var den jeg fiskede efter... Og hvor kikset er det så lige at mine hint har været misvisende - det beklager jeg altså meget!

Johannes:
Dit forslag er bestemt gangbart, og vil sikre at koden ikke fejler, men som du kan se af jta's kommentar, er det ikke en fuldstændig løsning, og en typisk (og mere dækkende kontruktion) kunne se sådan her ud:

if (ShowExtraOptions)
{
   // du loader kontrollen
   Control c = LoadControl("ExtraOptions.ascx");
   UxDynamicControlContainer.Controls.Add(c);

   // prøver at caste den til den specifikke type
   ExtraOptions extraOptions = c as ExtraOptions;
   if (extraOptions == null)
   {
       // er cast'et ikke gået godt - checkes for caching
       PartialCachingControl pc = c as PartialCachingControl;
       if (pc != null)
       {
           // og instansens sættes
           extraOptions = pc.CachedControl as ExtraOptions;
       }
   }

   // check for eksistens
   if (extraOptions != null)
   {
       // hvorefter vi kan gøre det vi vil
       ((ExtraOptions)extraOptions).WhatEver(...);
   }                      
}

Og grunden til at det bliver så hulens bikset er, at det i typiske scenarier vil være sjældent at .CachedControl IKKE er null.

Som jta skriver, skal kontrollen for det første være tilføjet til kontroltræet, men derudover returnerer .CachedControl kun kontrollen såfremt den er blevet skabt under det aktuelle request. Er kontrollen fundet i cachen - returnerer .CachedControl null.

Så jta:
Fedt at du kom med en mulig implementering, men jeg vil ikke anbefale at kaste en exception ved null, da det vil være ofte forekommende, og ikke en fejl.

Men - finurlighederne taget i betragtning - er det helt givet, at det er klart at foretrække at benytte en model som din til at pakke problematikken ind.

Benytter man ofte dynamisk loadede UserControls og caching, ville det måske være hensigtmæssigt at lægge metoden i en baseklasse og basere sine sider på den.

Tak for alle kommentarene - jeg synes "problemet" er vigtigt at være bekendt med (deraf quiz'en :o), og nu må jeg jo af med præmien, som må gå til Jakob Andersen for et helt korrekt svar :o)

Mvh

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Tuesday, June 26, 2007 10:49 AM by jepper

Så - jeg har lige prøvet at rette koden i jeres kommentarer, så der ikke er de der ekstra linieskift... Håber det er ok?

Men Jacob - jeg må jo have din adresse på en eller anden måde... Gider du skrive til mig via kontaktformularen?

Mvh

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Tuesday, June 26, 2007 10:56 AM by jta

Hej mig og Jakob Andersen er samme person jeg glemte bare at logge ind første gang, jeg skriver til dig via kommentarformularen :)

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Tuesday, June 26, 2007 11:05 AM by jepper

Ah - tænkte det nok.

Tak for bekræftelsen :o)

Mvh

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Tuesday, June 26, 2007 11:36 AM by jepper

Johannes - du mangler et svar :o)

Nej - tagMapping vil ikke kunne lave ballade her.

Dels kræver tagmapping, at du mapper til en type der i et eller andet niveau er en specialisering af den oprindelige type (du kan f.eks. ikke mappe fra en textbox til en calendar ol.), hvorfor du altid vil kunne upcaste til den oprindelige type, og dels vil din tagmapping ikke have effekt når kontrollen indsættes dynamisk.

Sidst men ikke mindst, så tror jeg faktisk du får problemer med at tagmappe en usercontrol.

Var det tilstrækkeligt?

Mvh

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Wednesday, June 27, 2007 7:48 AM by Johannes Hansen

Min reviderede Page_Load funktion som også checker outputcahce (som jeg i øvrigt lavede uafhængigt af de senere kommenterer :) ) ville se ud som følger:

protected void Page_Load(object sender, EventArgs e)
{
   if (ShowExtraOptions)
   {
       // du loader kontrollen
       Control control = LoadControl("ExtraOptions.ascx");
       // tilføj kontrollen til en placeholder
       //
       // bemærk at vi bruger "control" og ikke "extraOptions" instansen
       // da "extraOptions" kan være null selvom "control" ikke er det.
       //
       // vigtigt! kontrollen skal tilføjes inden man tilgår CachedControl værdien da denne ellers altid vil være null.
       Page.Controls.Add(control);
       // konverterer den til en typestærk kontrol med "as ExtraOptions"
       ExtraOptions extraOptions = null;
       // hvis controllen bruger outputcaching skal vi hente ExtraOptions kontrollen fra den cachede kontrol
       if (control is PartialCachingControl)
       {
           PartialCachingControl cachingControl = control as PartialCachingControl;
           extraOptions = cachingControl.CachedControl as ExtraOptions;
       }
       else
       {
           extraOptions = control as ExtraOptions;
       }
       // checker om "as ExtraOptions" resulterede i en instans og kalder noget ExtraOptions specifikt
       if (extraOptions != null)
       {
           extraOptions.SetDefaultChoice("Choice2");
       }
   }
}

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Wednesday, June 27, 2007 9:00 AM by Johannes Hansen

Ser frem til den næste quiz. ;)

# re: Quiz for de kaffeinteresserede : Er der nogle der kan regne den her ud ?

Wednesday, June 27, 2007 11:40 AM by jepper

Hej igen Johannes :o)

Fedt med dine sidste indlæg - der skal nok komme flere quiz'er ;o)

Mvh

Leave a Comment

(required) 
required 
(required) 

  
Enter Code Here: Required
 
Page view tracker