1 Comments

Kopam ja tako po ekstenzijama za Blogengine.net (njih čak 20-tak! Ako vam je do šale, pogledajte koliko ekstenzija ima WordPress!) i naiđem na dodatak koji ispisuje vrijeme potrebno za učitavanje/iscrtavanje stranice. Kako mi je jako važno da se stranica brzo učitava, da ne bi ovih 5 posjetioca dnevno (tjedno) čekali koju sekundu previše i otišli sa stranice, valja imati precizna vremena učitavanja!

Dodam ga na blog, oduševljeno gledam vremena ispod 0,1 sekunde i divim se kako je ASP.NET brz! Potom promijenim nešto u kodu, napravim refresh stranice, čekam 5-10 sekundi da se dinamička kompilacija izvrši, kada ono u footeru opet piše: “Page loaded in 0.00569874 seconds”. Znači, nešto ne valja. Ja sam čekao barem 50-tak puta više od prikazanog vremena. [more]

Ovo je kod od ekstenzije, koji se nalazi unutar ASCX user kontrole:

public partial class User_controls_PageLoadTime : System.Web.UI.UserControl
{
    private DateTime _start;
    private DateTime _end;

    protected void Page_Init(object sender, System.EventArgs e)
    {
        _start = DateTime.Now;
    }

    protected void Page_PreRender(object sender, System.EventArgs e)
    {
        _end = DateTime.Now;
        TimeSpan time = _end.Subtract(_start);
        lblPageLoadTime.Text = string.Format("Page loaded in {0} seconds.", time.TotalSeconds.ToString());
    }
}

Kao što se vidi iz priloženog, vrijeme se mjeri od početka izvršavanja OnInit događaja, pa do PreRender događaja.

Ako pogledamo sam trace stranice:

page-load-time

vidimo da ekstenzija ne radi dobro! Za stranicu je bilo potrebno sigurno oko 4-5 sekundi za iscrtavanje, ali OnInit i OnPreRender događaji su se desili negdje u srediti samoga izvršavanja. Pregledom nekoliko dostupnih primjera nađenih na internetu se vidi da se većina primjera uglavnom bazira na ova dva događaja, ali pregledom trace rezultata možemo vidjeti da je vrijeme između ta dva događaja samo manji dio ukupnog vremena izvršavanja page life cycle-a.

Zbog toga ovdje dajem svoj prijedlog jednostavnog koda za računanje vremena potrebnog za iscrtavanje stranice. Zasniva se na događaju Application_BeginRequest koji se definiran u Global.asax datoteci. Trenutno vrijeme se spremi u HttpContext.Items Dictionary objekt (korisno spremište za objekte koji će imati životni vijek trajanja jednog requesta – npr. za DataContext od LINQ2SQLa i slično, jer se sadržaj tog Dictionarya briše nakon kraja requesta).
Na kraju same stranice (u ovom slučaju u Master stranici), u inline kodu unutar aspx datoteke se dohvati trenutno vrijeme i oduzme od vremena unutar HttpContext.Items.

Kod iz Global.asax:

void Application_BeginRequest()
{
	if(HttpContext.Current.Request.Url.AbsolutePath.Contains(".aspx"))
		HttpContext.Current.Items["load"] = DateTime.Now;
}

Provjera “.aspx” stringa unutar request URL-a je zbog mogućnosti da se BeginRequest događaj pokreće i za druge datoteke osim .aspx stranica, poput CSS, JPG,…, što je specifično za lokalni developmnet server od Visual Studia (cassini, jel?) i za IIS7 koji isto daje ASP.NET frameworku requeste na svaku datoteku i folder (IIS6 hvata samo .aspx, .asmx i slične ekstenzija).

Kod na kraju aspx stranice:

Stranica učitana u <%= DateTime.Now.Subtract( (DateTime)HttpContext.Current.Items["load"] ).TotalSeconds.ToString() %> sekundi

(Molim zanemarite nedostatak provjere dali nešto uopće postoji unutar HttpContext.Items! Ovo može rezultirati sa poznatim yellow screen of death od asp.net)

Nakon ovakve implementacije mjerenja vremena, brojka mi osobno izgleda puno uvjerljivije: 

page-load-time2

primijetite na vrhu slike desno, “ … 2.6651 sekundi”, u odnosu na prijašnjih 0.21s.

Ako postoji neki drugi način mjerenja (osim dodavanja Http Modula, za to sam previše ljen, iako pogledom na primjer od Phil Haacka vidim da koristi jako slični način mjerenja, vjerojatno i marginalno točniji), ili je ova implementacija pogrešna u nečemu, molim vas da napišete komentar ili mi pošaljete mail.

6 Comments

Radeći administratorsko sučelje CMSa dobio sam grafički predložak dizajna koji je uključivao brojne okvire za module. Odmah sam natipkao nekoliko CSS klasa, aplicirao ih na dva-tri DIVa, i cijeli zadatak je bio gotov u pola sata. Da ne bude dosadno do kraja dana, valjalo je proanalizirati rješenje i pronaći način da se stvar malo zakomplicira!

Ako pogledamo HTML kod od okvira:

Naslov okvira
Obicni text...

implementacija sadržaja i opis CSS klasa je nebitan, ali je poanta da se svaki put moraju pisati točno ti DIVovi sa CSS klasama da bi se dobio željeni grafički rezultat. Da bi to izbjegli, cilj je upakirati HTML kod u zasebnu kontrolu koja bi bila pogodna za ponovno korištenje i primala bi samo čisti tekst koji mora prikazati.

Koliko je meni poznat rad u ASP.NETu, postoje dvije mogučnosti: Custom Controls ili User Controls. Custom Controls su previše kompleksne za ovaj zadatak, tako da ostaje samo druga opcija: opće poznate i svima dobro znane user kontrole, ilitiga ASCX kontrole, ili (ime po vlastitom izboru)

Ono što želimo dobiti izgleda ovako:


Naslov okvira

    ovdje ide puna vreća asp.net kontrola i sličnog

Želimo imati predloške unutar user kontrole, tako da je na jednom mjestu definiran grafički izgled (unutar ascx datoteke), a sadržaj kod samo upotrebe user kontrole. Kada dizajner dođe sa novim dizajnom, njegova implementacija mora biti jednostavna, brza i mora se svesti samo na promjenu unutar ascx datoteke.

Do sada nisam nikada koristio predloške unutar user kontrole, niti naišao na sličnu upotrebu user kontrole, pa nakon malo guglanja/binganja i isprobavanja stvar je konačno proradila. Evo kako napraviti svoju templateziranu user kontrolu:

 

1. dodati novu user kontrolu u projekt

2. dodati atribut ParseChildren na definiciju klase:

[ParseChildren(true)]
public partial class FrameContainer : System.Web.UI.UserControl

time smo rekli kompajleru da se unutar te user kontrole nalaze i child kontrole koje se moraju parsirati, odnosno izvršiti.

2. definiranje ITemplate propertya za svaki od predloška:

[TemplateContainer(typeof(ControlsContainer))]
[TemplateInstance(TemplateInstance.Single)]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate HeaderTemplate
{ get; set; }

[TemplateContainer(typeof(ControlsContainer))]
[TemplateInstance(TemplateInstance.Single)]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ContentTemplate
{ get; set; }

uz jednu napomenu: ako ima više ContentTemplate predložaka u deklaraciji, TemplateInstance.Single mora biti promjenjen u TemplateInstance.Multiple

3. TemplateContainer atribut opisuje tip kontrole koja je sadržana u predlošku, pa nju možemo jednostavno kreirati i dodati u projekt:

public class ControlsContainer : Control, INamingContainer { }

4. Izrada samog predloška unutar HTMLa user kontrole

PlaceHolderi su mjesta gdje ćemo ubaciti kontrole i HTML tekst koje smo dodali u deklaraciji user kontrole.

5. Renderiranje: kontrole koje smo u deklaraciji dodali u predložak moraju biti instancirane unutar OnInit eventa da bi ViewState ispravno funkcionirao.

protected override void OnInit(EventArgs e) 
{ 
	if (ContentTamplate != null) 
	{ 
		var c = new ControlsContainer(); 
		ContentTemplate.InstantiateIn(c); 
		ContentHolder.Controls.Add(c); 
	} 
	if (HeaderTemplate != null) 
	{ 
		var c = new ControlsContainer(); 
		HeaderTemplate.InstantiateIn(c); 
		HeaderHolder.Controls.Add(c); 
	} 
}

ControlsContainer je potrebno koristiti zbog implementacije INamingContainer interfacea, i zbog toga što ITemplate tip od HeaderContainera i ContentContainera ne možemo direktno instancirati u PlaceHolder.

 

I to je sve! Sasvim jednostavno!

download VisualStudio 2008 projekta sa primjerom