Hur debuggar man koden i autogenererade ASP.net klasser?
Förord
En vanlig fråga i ASP.NET forumet handlar om ASP.net kontrollrendering. Tänkte visa lite tricks och tips till ASP programmerare som är nya i ASP.net världen och till ASP.net programmerare som är nyfikna på hur ASP.net framework genererar källkod till de sidor som skall kompileras. Detta är lysande information som ASP.net bjuder på (helt gratis ;). Man kan lära sig massor av denna och den förklarar många av de problem som uppkommer när man inte riktigt förstår vad som händer bakom kulisserna.
Varför funkar inte inlinekod i mina kontroller?
Detta är en mycket vanlig fråga som ställs av nya ASP.net utvecklare. Problemet är nästan alltid relaterat till att man skjutit in <% %> (inline) kod i asp.net kontrollelementen. Senast i veckan såg jag denna fråga i forumet;Fråga:
[ASP.net kod]
blir
[HTML output]
istället för
[HTML output]
i webbläsaren (ie 6.0).
Är det någon som vet varför?
Går det inte att använda <%=calClass%> i asp:TableCell?
Slut på frågan…
Vad är det då som är problemet? För att förstå detta måste vi göra en liten djupdykning i hur ASP.net bygger upp källkoden till de autogenererade pageklasserna. Vi tar ett exempel med frågan ovan och skapar en sida med följande asp.net kod:
<%@ Page language="c#" AutoEventWireup="false" debug=”true”%>
example
<%
String theStyle = "myCustomStyle";
%>
<%="This will be written in the method RenderControl1"%>
<%Response.Write("This will generate a wanted error, no ;")%>
När vi gör en http request mot denna aspx sida kommer följande att hända. (aningen förenklat)
Vi första access
1. Baserat på filtypen (.aspx) kommer en given handler factory parsa aspx filen och dynamiskt generera ett parseträd. Detta träd är inte det kontrollträd som kommer byggas i ett senare moment utan ett liknande träd av control builders. ASP.net genererar dynamiskt källkod baserat på denna trädstruktur. Vilket språk som används beror på vad ni satt för språk i Page direktivets language attribut.
2. Den genererade koden (som ärver från Page) kompileras och sparas i ASP.net cachen.
3. En instans av denna dynamiskt skapade klass skapas och exekveras via en IHTTPHandler referens av den givna handler factoryn.
4. Den instansierade klassens FrameWorkInitialize metod körs (anropas från IHTTPHandler.ProcessRequest() implementationen i Page) och denna bygger upp det kontrollträd som sidan består av. När detta är klart körs RenderControl på Page klassen som i sin tur anropar Render på alla childkontroller i trädstrukturen. Det är viktigt att förstå att för varje request till en asp.net sida så byggs detta kontrollträd upp, renderas och slits ner. Detta är grunden för sidexekvering i ASP.NET. All kod som är angiven som <%= %> (inline kod) kommer att exekveras efter det att kontrollträdet renderats till HTML och skickats ut till response strömmen.
5. Resultatet (ren HTML) returneras till klienten
Vid efterföljande accesser till samma sida
1. Klienten gör en andra request till samma sida.
2. HTTP Handler factoryn kollar om aktuell klass redan finns lagrad i asp.net cachen, den upptäcker att så är fallet och laddar denna.
3. En ny instans av klassen skapas och används som http handler i denna request.
4. Resultatet returneras till klienten
Svaret på frågan är alltså följande. All inline kod (kod inom <% %>) kommer att köras i en speciell metod som körs efter det att kontrollträdet (där tabellkontrollen ingår) har renderats till vanlig html. Det är alltså redan kört (kontrollen är ju redan renderad och på väg som HTML till klienten). Det som kommer att hända är att det som står inom <%= %> tolkas som ett rent attribut till cellkontrollen. Vi skall titta på koden för detta om ett litet tag.
För att kolla in hur koden för den autogenererade sidan ser ut sätter vi debug=”true” i Page direktivet och skriver in ett önskat fel i sidan. Jag valde att skriva Response.Write() och inte avsluta denna med ett ;. Efter detta är det bara att köra sidan som kommer att generera ett kompileringsfel och vi får möjlighet att kolla in den automatiskt genererade koden genom att klicka på en länk som heter Show complete compilation source: (längst ner på den errorsida som visas när sidan körs).
Kollar vi in koden ser vi följande:
Listar all kod som genereras, det kommer mer förklarande text under denna, scrolla ner! :)
Line 1: //------------------------------------------------------------------------------
Line 2: //
Line 3: // This code was generated by a tool.
Line 4: // Runtime Version: 1.1.4322.573
Line 5: //
Line 6: // Changes to this file may cause incorrect behavior and will be lost if
Line 7: // the code is regenerated.
Line 8: //
Line 9: //------------------------------------------------------------------------------
Line 10:
Line 11: namespace ASP {
Line 12: using System;
Line 13: using System.Collections;
Line 14: using System.Collections.Specialized;
Line 15: using System.Configuration;
Line 16: using System.Text;
Line 17: using System.Text.RegularExpressions;
Line 18: using System.Web;
Line 19: using System.Web.Caching;
Line 20: using System.Web.SessionState;
Line 21: using System.Web.Security;
Line 22: using System.Web.UI;
Line 23: using System.Web.UI.WebControls;
Line 24: using System.Web.UI.HtmlControls;
Line 25: using ASP;
Line 26:
Line 27:
Line 28: [System.Runtime.CompilerServices.CompilerGlobalScopeAttribute()]
Line 29: public class example_aspx : System.Web.UI.Page, System.Web.SessionState.IRequiresSessionState {
Line 30:
Line 31:
Line 32: #line 18 "http://localhost/MVControlContainer/example.aspx"
Line 33: private System.Web.UI.WebControls.TableCell __control5;
Line 34:
Line 35: #line default
Line 36: #line hidden
Line 37:
Line 38:
Line 39: #line 17 "http://localhost/MVControlContainer/example.aspx"
Line 40: private System.Web.UI.WebControls.TableCellCollection __control4;
Line 41:
Line 42: #line default
Line 43: #line hidden
Line 44:
Line 45:
Line 46: #line 17 "http://localhost/MVControlContainer/example.aspx"
Line 47: private System.Web.UI.WebControls.TableRow __control3;
Line 48:
Line 49: #line default
Line 50: #line hidden
Line 51:
Line 52:
Line 53: #line 16 "http://localhost/MVControlContainer/example.aspx"
Line 54: private System.Web.UI.WebControls.TableRowCollection __control2;
Line 55:
Line 56: #line default
Line 57: #line hidden
Line 58:
Line 59:
Line 60: #line 16 "http://localhost/MVControlContainer/example.aspx"
Line 61: protected System.Web.UI.WebControls.Table tblTable;
Line 62:
Line 63: #line default
Line 64: #line hidden
Line 65:
Line 66:
Line 67: #line 15 "http://localhost/MVControlContainer/example.aspx"
Line 68: protected System.Web.UI.HtmlControls.HtmlForm Form1;
Line 69:
Line 70: #line default
Line 71: #line hidden
Line 72:
Line 73: private static bool __initialized = false;
Line 74:
Line 75: private static object __stringResource;
Line 76:
Line 77: private static System.Collections.ArrayList __fileDependencies;
Line 78:
Line 79: public example_aspx() {
Line 80: System.Collections.ArrayList dependencies;
Line 81: if ((ASP.example_aspx.__initialized == false)) {
Line 82: ASP.example_aspx.__stringResource = System.Web.UI.TemplateControl.ReadStringResource(typeof(ASP.example_aspx));
Line 83: dependencies = new System.Collections.ArrayList();
Line 84: dependencies.Add("c:\\inetpub\\wwwroot\\MVControlContainer\\example.aspx");
Line 85: ASP.example_aspx.__fileDependencies = dependencies;
Line 86: ASP.example_aspx.__initialized = true;
Line 87: }
Line 88: this.Server.ScriptTimeout = 30000000;
Line 89: }
Line 90:
Line 91: protected override bool SupportAutoEvents {
Line 92: get {
Line 93: return false;
Line 94: }
Line 95: }
Line 96:
Line 97: protected ASP.Global_asax ApplicationInstance {
Line 98: get {
Line 99: return ((ASP.Global_asax)(this.Context.ApplicationInstance));
Line 100: }
Line 101: }
Line 102:
Line 103: public override string TemplateSourceDirectory {
Line 104: get {
Line 105: return "/MVControlContainer";
Line 106: }
Line 107: }
Line 108:
Line 109: private System.Web.UI.Control __BuildControl__control5() {
Line 110: System.Web.UI.WebControls.TableCell __ctrl;
Line 111:
Line 112: #line 18 "http://localhost/MVControlContainer/example.aspx"
Line 113: __ctrl = new System.Web.UI.WebControls.TableCell();
Line 114:
Line 115: #line default
Line 116: #line hidden
Line 117: this.__control5 = __ctrl;
Line 118:
Line 119: #line 18 "http://localhost/MVControlContainer/example.aspx"
Line 120: __ctrl.Text = "Testcell";
Line 121:
Line 122: #line default
Line 123: #line hidden
Line 124:
Line 125: #line 18 "http://localhost/MVControlContainer/example.aspx"
Line 126: ((System.Web.UI.IAttributeAccessor)(__ctrl)).SetAttribute("class", "<%=theStyle%>");
Line 127:
Line 128: #line default
Line 129: #line hidden
Line 130: return __ctrl;
Line 131: }
Line 132:
Line 133: private void __BuildControl__control4(System.Web.UI.WebControls.TableCellCollection __ctrl) {
Line 134:
Line 135: #line 17 "http://localhost/MVControlContainer/example.aspx"
Line 136: this.__BuildControl__control5();
Line 137:
Line 138: #line default
Line 139: #line hidden
Line 140:
Line 141: #line 17 "http://localhost/MVControlContainer/example.aspx"
Line 142: __ctrl.Add(this.__control5);
Line 143:
Line 144: #line default
Line 145: #line hidden
Line 146: }
Line 147:
Line 148: private System.Web.UI.Control __BuildControl__control3() {
Line 149: System.Web.UI.WebControls.TableRow __ctrl;
Line 150:
Line 151: #line 17 "http://localhost/MVControlContainer/example.aspx"
Line 152: __ctrl = new System.Web.UI.WebControls.TableRow();
Line 153:
Line 154: #line default
Line 155: #line hidden
Line 156: this.__control3 = __ctrl;
Line 157:
Line 158: #line 17 "http://localhost/MVControlContainer/example.aspx"
Line 159: this.__BuildControl__control4(__ctrl.Cells);
Line 160:
Line 161: #line default
Line 162: #line hidden
Line 163: return __ctrl;
Line 164: }
Line 165:
Line 166: private void __BuildControl__control2(System.Web.UI.WebControls.TableRowCollection __ctrl) {
Line 167:
Line 168: #line 16 "http://localhost/MVControlContainer/example.aspx"
Line 169: this.__BuildControl__control3();
Line 170:
Line 171: #line default
Line 172: #line hidden
Line 173:
Line 174: #line 16 "http://localhost/MVControlContainer/example.aspx"
Line 175: __ctrl.Add(this.__control3);
Line 176:
Line 177: #line default
Line 178: #line hidden
Line 179: }
Line 180:
Line 181: private System.Web.UI.Control __BuildControltblTable() {
Line 182: System.Web.UI.WebControls.Table __ctrl;
Line 183:
Line 184: #line 16 "http://localhost/MVControlContainer/example.aspx"
Line 185: __ctrl = new System.Web.UI.WebControls.Table();
Line 186:
Line 187: #line default
Line 188: #line hidden
Line 189: this.tblTable = __ctrl;
Line 190:
Line 191: #line 16 "http://localhost/MVControlContainer/example.aspx"
Line 192: __ctrl.ID = "tblTable";
Line 193:
Line 194: #line default
Line 195: #line hidden
Line 196:
Line 197: #line 16 "http://localhost/MVControlContainer/example.aspx"
Line 198: this.__BuildControl__control2(__ctrl.Rows);
Line 199:
Line 200: #line default
Line 201: #line hidden
Line 202: return __ctrl;
Line 203: }
Line 204:
Line 205: private System.Web.UI.Control __BuildControlForm1() {
Line 206: System.Web.UI.HtmlControls.HtmlForm __ctrl;
Line 207:
Line 208: #line 15 "http://localhost/MVControlContainer/example.aspx"
Line 209: __ctrl = new System.Web.UI.HtmlControls.HtmlForm();
Line 210:
Line 211: #line default
Line 212: #line hidden
Line 213: this.Form1 = __ctrl;
Line 214:
Line 215: #line 15 "http://localhost/MVControlContainer/example.aspx"
Line 216: __ctrl.ID = "Form1";
Line 217:
Line 218: #line default
Line 219: #line hidden
Line 220:
Line 221: #line 15 "http://localhost/MVControlContainer/example.aspx"
Line 222: __ctrl.Method = "post";
Line 223:
Line 224: #line default
Line 225: #line hidden
Line 226: System.Web.UI.IParserAccessor __parser = ((System.Web.UI.IParserAccessor)(__ctrl));
Line 227:
Line 228: #line 15 "http://localhost/MVControlContainer/example.aspx"
Line 229: __parser.AddParsedSubObject(new System.Web.UI.LiteralControl("\r\n\t\t\t"));
Line 230:
Line 231: #line default
Line 232: #line hidden
Line 233:
Line 234: #line 15 "http://localhost/MVControlContainer/example.aspx"
Line 235: this.__BuildControltblTable();
Line 236:
Line 237: #line default
Line 238: #line hidden
Line 239:
Line 240: #line 15 "http://localhost/MVControlContainer/example.aspx"
Line 241: __parser.AddParsedSubObject(this.tblTable);
Line 242:
Line 243: #line default
Line 244: #line hidden
Line 245:
Line 246: #line 15 "http://localhost/MVControlContainer/example.aspx"
Line 247: __parser.AddParsedSubObject(new System.Web.UI.LiteralControl("\r\n\t\t"));
Line 248:
Line 249: #line default
Line 250: #line hidden
Line 251: return __ctrl;
Line 252: }
Line 253:
Line 254: private void __BuildControlTree(System.Web.UI.Control __ctrl) {
Line 255:
Line 256: #line 1 "http://localhost/MVControlContainer/example.aspx"
Line 257: this.__BuildControlForm1();
Line 258:
Line 259: #line default
Line 260: #line hidden
Line 261: System.Web.UI.IParserAccessor __parser = ((System.Web.UI.IParserAccessor)(__ctrl));
Line 262:
Line 263: #line 1 "http://localhost/MVControlContainer/example.aspx"
Line 264: __parser.AddParsedSubObject(this.Form1);
Line 265:
Line 266: #line default
Line 267: #line hidden
Line 268: __ctrl.SetRenderMethodDelegate(new System.Web.UI.RenderMethod(this.__Render__control1));
Line 269: }
Line 270:
Line 271: private void __Render__control1(System.Web.UI.HtmlTextWriter __output, System.Web.UI.Control parameterContainer) {
Line 272: this.WriteUTF8ResourceString(__output, 0, 385, true);
Line 273:
Line 274: #line 11 "http://localhost/MVControlContainer/example.aspx"
Line 275:
Line 276: String theStyle = "myCustomStyle";
Line 277:
Line 278:
Line 279: #line default
Line 280: #line hidden
Line 281: __output.Write("\r\n\t\r\n\t\t");
Line 282:
Line 283: #line 15 "http://localhost/MVControlContainer/example.aspx"
Line 284: parameterContainer.Controls[0].RenderControl(__output);
Line 285:
Line 286: #line default
Line 287: #line hidden
Line 288: __output.Write("\r\n\t\r\n\t\r\n\t");
Line 289:
Line 290: #line 24 "http://localhost/MVControlContainer/example.aspx"
Line 291: __output.Write("This will be written in the method RenderControl1");
Line 292:
Line 293: #line default
Line 294: #line hidden
Line 295: __output.Write("\r\n\t\r\n\t");
Line 296:
Line 297: #line 26 "http://localhost/MVControlContainer/example.aspx"
Line 298: Response.Write("This will generate a wanted error, no ;")
Line 299:
Line 300: #line default
Line 301: #line hidden
Line 302: __output.Write("\r\n