More details on code behind v2

I recently posted on the upcoming changes to the code separation model coming in beta 2 of ASP.NET 2.0, and was left wondering about some of the specifics of the implementation. I finally had a chance to try out the most recent beta 2 bits, and can now report more fully on the implementation change.
 
Here's an example of a simple page (MyPage.aspx) with a couple controls on it that you might author:
 
<%@ Page Language="C#" CodeFile="MyPage.aspx.cs"
               Inherits="PS.AspDotNet20.MyPage" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
  <body>
    <form id="form1" runat="server">
      Enter your name: <asp:TextBox ID="nameTextBox" runat="server" /><br />
      <asp:Button ID="enterButton" runat="server"
                       Text="Enter" OnClick="enterButton_Click" />
      <br />
      <asp:Label ID="messageLabel" runat="server" />
    </form>
  </body>
</html>
 
And here's the corresponding code-behind file (MyPage.aspx.cs):
 
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
 
namespace PS.AspDotNet20
{
  public partial class MyPage : System.Web.UI.Page
  {
    protected void Page_Load(object sender, EventArgs e)
    {
    }
 
    protected void enterButton_Click(object sender, EventArgs e)
    {
      messageLabel.Text = "Hello " + nameTextBox.Text + "!";
    }
  }
}
 
Now, when ASP.NET 2.0 handles a request for MyPage.aspx, it will generate 2 classes. One will be a sibling partial class to the code behind class we authored in MyPage.aspx.cs. In order to generate this partial class, ASP.NET must create a class with the exact class name and namespace as our code behind class so they can be merged at compilation. Here's what it looks like:
 
namespace PS.AspDotNet20
{
  public partial class MyPage : IRequiresSessionState
  {
    protected global::System.Web.UI.WebControls.TextBox nameTextBox;
    protected global::System.Web.UI.WebControls.Button enterButton;
    protected global::System.Web.UI.WebControls.Label messageLabel;
    protected global::System.Web.UI.HtmlControls.HtmlForm form1;
    protected System.Web.Profile.DefaultProfile Profile
    {
      get
      {
        return ((System.Web.Profile.DefaultProfile)(this.Context.Profile));
      }
    }
    protected System.Web.HttpApplication ApplicationInstance
    {
      get
      {
        return ((System.Web.HttpApplication)(this.Context.ApplicationInstance));
      }
    }
  }
}
 
Notice that all of the control member variables that you used to have to declare (or let the IDE declare for you) in your code behind class are now generated automatically in your code behind class' sibling partial class. This class also contains the typesafe property accessors to your Profile and Application object.
 
The other class that ASP.NET 2.0 will generate is the result of parsing the .aspx file, and looks similar to the file that is produced today in ASP.NET 1.1 from .aspx page parsing. Note that it derives from your code behind class (and its sibling partial class). Interestingly, this class is placed in the same temporary source code file as the partial sibling class above. Here's what it looks like for the for MyPage.aspx file:
 
namespace ASP
{
  public class MyPage_aspx : global::PS.AspDotNet20.MyPage
  {
    private static bool @__initialized = false;
    private static object @__fileDependencies;
    public MyPage_aspx()
    {
      string[] dependencies;
      AppRelativeVirtualPath = "~/MyPage.aspx";
      if ((global::ASP.MyPage_aspx.@__initialized == false))
      {
        dependencies = new string[2];
        dependencies[0] = "~/MyPage.aspx";
        dependencies[1] = "~/MyPage.aspx.cs";
        global::ASP.MyPage_aspx.@__fileDependencies = this.GetWrappedFileDependencies(dependencies);
        global::ASP.MyPage_aspx.@__initialized = true;
      }
      this.Server.ScriptTimeout = 30000000;
    }
    private global::System.Web.UI.WebControls.TextBox @__BuildControlnameTextBox()
    {
      global::System.Web.UI.WebControls.TextBox @__ctrl;
      @__ctrl = new global::System.Web.UI.WebControls.TextBox();
      this.nameTextBox = @__ctrl;
      @__ctrl.ApplyStyleSheetSkin(this);
      @__ctrl.ID = "nameTextBox";
      return @__ctrl;
    }
    private global::System.Web.UI.WebControls.Button @__BuildControlenterButton()
    {
      global::System.Web.UI.WebControls.Button @__ctrl;
      @__ctrl = new global::System.Web.UI.WebControls.Button();
      this.enterButton = @__ctrl;
      @__ctrl.ApplyStyleSheetSkin(this);
      @__ctrl.ID = "enterButton";
      @__ctrl.Text = "Enter";
      @__ctrl.Click += new System.EventHandler(this.enterButton_Click);
      return @__ctrl;
    }
    private global::System.Web.UI.WebControls.Label @__BuildControlmessageLabel()
    {
      global::System.Web.UI.WebControls.Label @__ctrl;
      @__ctrl = new global::System.Web.UI.WebControls.Label();
      this.messageLabel = @__ctrl;
      @__ctrl.ApplyStyleSheetSkin(this);
      @__ctrl.ID = "messageLabel";
      return @__ctrl;
    }
    private global::System.Web.UI.HtmlControls.HtmlForm @__BuildControlform1()
    {
      global::System.Web.UI.HtmlControls.HtmlForm @__ctrl;
      @__ctrl = new global::System.Web.UI.HtmlControls.HtmlForm();
      this.form1 = @__ctrl;
      @__ctrl.ID = "form1";
      System.Web.UI.IParserAccessor @__parser = ((System.Web.UI.IParserAccessor)(@__ctrl));
      @__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("\r\n Enter your name: "));
      global::System.Web.UI.WebControls.TextBox @__ctrl1;
      @__ctrl1 = this.@__BuildControlnameTextBox();
      @__parser.AddParsedSubObject(@__ctrl1);
      @__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("<br />\r\n "));
      global::System.Web.UI.WebControls.Button @__ctrl2;
      @__ctrl2 = this.@__BuildControlenterButton();
      @__parser.AddParsedSubObject(@__ctrl2);
      @__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("\r\n <br />\r\n "));
      global::System.Web.UI.WebControls.Label @__ctrl3;
      @__ctrl3 = this.@__BuildControlmessageLabel();
      @__parser.AddParsedSubObject(@__ctrl3);
      @__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("\r\n "));
      return @__ctrl;
    }
    private void @__BuildControlTree(MyPage_aspx @__ctrl)
    {
      System.Web.UI.IParserAccessor @__parser = ((System.Web.UI.IParserAccessor)(@__ctrl));
      @__parser.AddParsedSubObject(new System.Web.UI.LiteralControl(
                    "\r\n\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\" >\r\n<body>\r\n "));
      global::System.Web.UI.HtmlControls.HtmlForm @__ctrl1;
      @__ctrl1 = this.@__BuildControlform1();
      @__parser.AddParsedSubObject(@__ctrl1);
      @__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("\r\n</body>\r\n</html>\r\n"));
    }
    protected override void FrameworkInitialize()
    {
      base.FrameworkInitialize();
      this.AddWrappedFileDependencies(global::ASP.MyPage_aspx.@__fileDependencies);
      this.Request.ValidateInput();
    }
    public override int GetTypeHashCode()
    {
      return -731145279;
    }
  }
}
 
So for each .aspx file with code behind you author in ASP.NET 2.0 (beta 2 and beyond) ASP.NET is going to generate a partial class to be merged with the partial class you define in your code behind file. These two partial classes will be merged into a single class definition at compilation, and will serve as the base class to the class generated from the .aspx file itself.
 
One other aspect I found interesting was that ASP.NET 2.0 actually copies the contents of your code behind class into a new source code file adjacent to the two generated class definitions. This is necessary so that a single compilation can be issued to merge the partial classes into a single code behind base class.
 
So, the advantage to this model, as I see it, is ease of migration - mostly mental migration. Developers accustomed to writing base classes for their .aspx files will continue to do so, with the one change of having the control member variables being generated automatically in a sibling partial class (obviating the need to add them by hand or through the IDE). Note that if you use the src= attribute instead of the CodeFile= attribute to specify your code behind file (or if you specify neither and deploy the compiled code behind class assembly in the /bin directory), it will revert to using 1.1-style code behind, and will not generate the sibling partial class with the control declarations. Also note that if you try to convert a 1.1-style code behind file to use this technique by specifying a CodeFile= attribute, you must remove your control declarations or you will get a compiler error saying they are redundantly defined.
 
This model also makes it possible to use the new code behind model with a shared common base class, as many people do today. There is one claim I have seen made that this model also enables the pre-compilation of the code behind classes, but this is just not true from what I have seen. You must specify the source code file of your code behind partial class so that it can be merged with the generated sibling partial class during ASP.NET 2.0's compilation. You can, of course, precompile the entire site using the new aspnet_compiler.exe utility - but that's a different thing.
 
Anyway, it's good to finally see how they implemented it - I hope this post helps clear up any confusion over the new code behind model.

Posted Jan 18 2005, 11:39 AM by fritz-onion
Filed under:

Comments

TheChaseMan's Frenetic SoapBox wrote More Changes to ASP.NET v2.0
on 01-18-2005 10:32 AM
Paul Wilson wrote re: More details on code behind v2
on 01-18-2005 12:08 PM
This really makes me think they should have just left code-behind as it was in v1.*. I was initially opposed to this when it was introduced back at the first private preview in October 2003, but I (and others like me) relented when the team insisted the code-behind model was too "brittle". While there was certainly truth in this, and certainly beginners in OO have issues understanding code-behind, it just doesn't look like they've succeeded in the end here. Now it appears to be a monstrosity that's not OO, not simple, and not even as functional as the original model which at least allowed you to pre-compile the code-behind while leaving the design non-compiled so designers could modify small things in it if necessary.
Paul Wilson's .NET Blog wrote The Latest Crap on Code-BePart in ASP.NET v2.0
on 01-18-2005 12:17 PM
Sergio Pereira wrote re: More details on code behind v2
on 01-18-2005 12:50 PM
I still think that this whole partial class thing in the code-behing is just to show off partial classes :) C'mon... All this mess just to avoid those protected members? Is there some other more important advantage ? If we would eventually get page parser/compilation errors before (our mistake, of course), now with more files being generated I'm affraid it represents more space for those errors to happen.
On the other hand, this should make me more comfortable that the IDE will not mess with my code so much. This should be especially good in windows forms.
David Hayden wrote re: More details on code behind v2
on 01-18-2005 1:59 PM
I wouldn't go as far as Paul and suggest this is possibly a step backwards, but certainly if the only advantage to this model is the lack of having to declare protected control members, this is certainly a trivial enhancement.

I thought the payoff in this model of using partial classes was in the ability to use code generation to help generate parts of a class in separate source files. This way you can have code generated parts of a class and customized parts of a class in separate files that can be merged to generate the complete class. I see this opening a lot of doors to IDE and tool vendors in helping developers be more productive and causing less problems with developers stepping on code generated portions of classes. Am I wrong or missing the big picture?
David Taylor wrote re: More details on code behind v2
on 01-18-2005 2:26 PM
>There is one claim I have seen made that this model also enables the pre-compilation of the code behind classes....(cut).

But it does allow this Fritz. This is because your code-behind and original .ASPX file (both partial classes) will be compiled together into a single pre-compiled class. This class then becomes your page's bass class and at runtime your ASPX page will inherit from this base class.

Then at runtime you can edit your ASPX file, (like you can in 1.x) and it will simply cause a recompile (again inheriting from your original base class).

You are probably just confused because you figure "but what if I add a few extra controls to my ASPX file after the base class has been pre-compiled? They will not be added to the base class!". That is perfectly correct, but not a problem, because your base class never reference those new controls anyway.

In fact this is exactly the same situation as we have today in V1.x with codebehind if you add a new control to your ASPX file using nodepad after the code behind was pre-compiled by Visual Studio.

This is actually quite simple, and I think people are just confused because the ASP.NET team changed the way this is done mid-way through the beta process. But the end result will be quite elegant.
Brian Scott wrote re: More details on code behind v2
on 01-18-2005 2:32 PM
I never deploy .cs files to the web server and I usually inherit my code-behinds from a custom base page that inherits from System.Web.UI.Page. Will I still be able to do that without any trouble? I like and am very comfortable with that model.

Also, I don't know how comfortable I am with not being able to see the declarations for the controls I'm using in the file where I am using them. I sometimes do a lot of glacing at the top of the file if I forget the name of a control I need to use. I just don't think I like the idea. There doesn't seem to be enough payoff.
David Taylor wrote re: More details on code behind v2
on 01-18-2005 2:54 PM
Brian, the answer is "Yes you can do what you have done in the past without any trouble". If you have forgotten what controls you have access to just press "Ctrl-space" or type "this." and intellisense will show you.

Fritz - here is some additional info I posted on Pauls Site:

I should clarify one last possible point of confusion:

Fritz was probably thinking that it would be impossible to pre-compile your base page partial class, because it needs to be merged with a partial class with control definitions generated from your ASPX file; And this is completely correct...you DO need that ASPX file to be defined first in order to pre-compile your base class.

But the point here is that whilst you do need an ASPX file containing the control definitions in order to pre-compile your base page class; at any time after this, you can edit the ASPX file like you can today in V1.x and it will be recompiled on the fly inheriting from your pre-compiled base class.

The main question is "Is it better the controls are defined in the .cs file imperatively , or the ASPX file declaratively". Hopefully everyone agrees the latter is better.

And guess what? If you disagree, and just want to define the entire base-class in code without needing an ASPX file to compile against (partially) - you can just do that by writing the class in exactly the same way as you would in V1.x. Just forget all this partial stuff and write an entire base class that inherits from Page.

I hope we can get some agreement that this is "all good" and that Paul can edit his blog entry and remove the word "crap" ;-)

Regards,

David Taylor
Fritz Onion wrote re: More details on code behind v2
on 01-18-2005 3:01 PM
David - perhaps you can clarify for me, in what way is the code-behind file and it's generated partial class sibling precompiled? The sibling partial class is generated by ASP.NET on request and then compiled together with the code-behind class to form the base class for the ASPX page. Unless there is some option to generate this sibling class during a local compilation before deployment, I don't see how anyone can call it precompiled. Separately compiled, perhaps (although in my tests, the sibling partial class and the ASPX class were placed in the same file, preventing separate compilation even).
Fritz Onion wrote re: More details on code behind v2
on 01-18-2005 3:08 PM
Btw - I'm reasonably happy with the new model. I think the advantage of not having to declare control member variables is huge. I think making the inheritance from System.Web.UI.Page explicit in the code-behind class is good. However, I still claim that the new model does not enable pre-compilation of the code behind classes, and I don't think I'm confused :)
haacked@gmail.com (Haacked) wrote RE: More details on code behind v2
on 01-18-2005 3:15 PM
This is interesting. I thought that the declaration in the .aspx file was going to be the actual declaration ala XAML. Thus the partial class would be the .aspx file. But I see they've moved away from that for obvious reasons.
Sahil Malik wrote re: More details on code behind v2
on 01-19-2005 8:23 AM
This is good info Fritz .. thanks for sharing.
GuyIncognito wrote re: More details on code behind v2
on 01-19-2005 3:56 PM
Fritz, thanks for the updated info. What about us guys with the beta 1 bits who'd like to start crafting our ASP.NET 2.0 applications? Should we just wait until the beta 2 release (which I hear is supposed to be in spring) or is there a way we can "prepare" for the changes to the "code-behind/beside" model?

Fritz Onion wrote re: More details on code behind v2
on 01-19-2005 4:14 PM
I would just go ahead and build with Beta 1, and then make the necessary changes when beta 2 comes out. They should be pretty mechanical - just add the Page base class to your code behind files, change CompileWith= and ClassName= to CodeFile= and Inherits=. Also, try and make your code behind methods protected instead of private.
David Taylor wrote re: More details on code behind v2
on 01-19-2005 10:40 PM
Fritz,

You compile your ASPX page and your partial class into a precompiled class and end up with a precompiled DLL 'exactly' the same as you had in V1.x from a reflection standpoint. By that I mean if you looked at the result using a reflector it would be about the same (minus all the other changes they made in 2.0).

Maybe the piece you are missing is the compiler....Obviously you cannot use the cs.exe compiler as it would complain because it does not understand the partial class defines by the APSX markup. So the ASP.NET team are providing a special command line compiler that can precompile your ASPX together with your C# partial class.

I forget the name of the compiler (aspxxxx.exe?) but ScottGu blogged about it last October or November.

This will seriously all make sence once Beta 2 comes out and the ASP.NET team have more time to write articles and blog ;-)

David Taylor
Fritz Onion wrote re: More details on code behind v2
on 01-20-2005 3:12 AM
Thanks David, it was the compiler piece I was missing. It makes perfect sense that they could provide their own compiler (and build it into VS.NET) that generates the sibling partial class and combines them into a single assembly for deployment.
It will be interesting to see how they present this in the IDE - perhaps a special deployment wizard that gives you the option of precompiling the code behind files.
Anyway, I agree that it will be much clearer once beta 2 is out.
David Taylor wrote re: More details on code behind v2
on 01-20-2005 4:24 AM
BTW Fritz,

If you have Lutz's reflector, load up the November CTP assemblies and search for this class: PageAsyncTask

This will set you on a trail of reflection that will be exciting (and probably waste half a day as it did for me).

I wrote a little web app sample after reading your earlier blog entry...but dont want to say much more unless I spoil the suprise....other than that I dont believe the implementation is even 100% complete in the November CTP.

Once you realize where this is going, you will be *very* excited about Beta 2

I might post my own Blog entry about this....but will give you a few days first to see if you can beat me to it.

;-)

David Taylor
Fritz Onion wrote re: More details on code behind v2
on 01-20-2005 4:43 AM
PageAsyncTask - intriguing indeed! I see I'm going to have to spend some quality time with this feature :)
Jeffrey Palermo wrote re: More details on code behind v2
on 01-21-2005 11:00 AM
I believe this is an improvement from Beta 1 where I had a hard time using public properties of my custom user control code-behind classes. Also with the added editor support for intellisense in the markup view I will now do two things.

1. Inherit all of my pages from a single base class to handle everything common.
2. Write the little code to use my web controls in a <script runat=server/> tag.

I already make a practice of not writing any logic whatsoever in my aspx.cs files, so they are really fluff. I would love the opportunity to shun them and keep my presentation in the aspx and my code in my base classes where it belongs. And, of course, precompilation can compile everything.
LA.Net wrote Beta 2- mais informa
on 01-22-2005 2:27 AM
David Taylor wrote re: More details on code behind v2
on 01-22-2005 9:19 PM
Hi Fritz,

Paul was starting to really get on my case...So I actually loaded up the CTP to find the information for him (Paul needed to know this stuff given his position as a leader in the .NET community)....So here are the details reposted for you (below).

Btw Fritz...To keep you going regarding PageAsyncTask, here is another hint:
Page.RegisterAsyncTask(myTask);
;-)
-----
Paul,

I did convince Fritz. Anyway, load the November CTP onto a machine (do not try this with the December CTP which has an older framework). From a .NET command line prompt (or in the .NET 2 directory) type:

aspnet_compiler.exe /?

To completely precompile your app, removing all source (even ASPX source) do this:
aspnet_compiler.exe -v /MyApp -p c:\MyApp c:\Output

To do what I have been "trying" to explain use the -u option (for Updatable), as in:
aspnet_compiler.exe -u -v /MyApp -p c:\MyApp c:\Output

This will basically precompile your partial class against your ASPX file, leaving you with a precompiled DLL as per what you have with .NET 1.1, and leaving your ASPX page alone so it can be edited after deployment (as per the 1.x deployment model).

Obviously.....You need to use a new compiler, because the csc.exe compiler (and vb compiler) will not understand how to compile the ASPX into a partial class....

I hope that helps clear things up.

Note the compiler is a core part of .NET 2, not a special thing shipping with Visual Studio

---
Fritz Onion wrote re: More details on code behind v2
on 01-23-2005 4:25 AM
Confirmed - the /u option generates precompiled code-behind classes merging your partial class with the ASP.NET-generated sibling partial class, but leaves the .aspx files intact for updating purposes. The IDE option for this is a checkbox in the Build/Publish Web Site dialog that says 'Allow this pre-compiled site to be updatable'.
So as David points out, this model really gives us everything we had before with a less brittle and more flexible implementation.
I admit, it feels a bit overly complex for what it is, but to provide all of the compilation and deployment options they are providing, I don't see a simpler solution.

I was chatting with <a href="http://staff.develop.com/ballen/blog/">Brock Allen" about this the other day, and he pointed out one potential advantage of the beta 1 model over the beta 2 model, which is the ability to specify an alternate base class for all pages in your config file at some later point in time. In the new model, you'd have to manually update all of your code-behind classes to point to your new base class.
Sahil Malik wrote re: More details on code behind v2
on 01-25-2005 1:33 PM
Fritz,

I hope you see my blog comment, sorry I'm a bit late to the party.

With the compilation model change, I can precompile and deploy aspx'es and ascx'es to IIS - that's awesome.

My question is - can I mark the various controls on my base page ascx/aspx as a protected/private/public/internal .. etc.?

I'd appreciate your reply :)

- SM
Fritz Onion wrote re: More details on code behind v2
on 01-26-2005 8:59 AM
Sahil - there's no way that I have seen to change the protection level of the control declarations, they seem to always be protected.

-Fritz
Sahil Malik wrote re: More details on code behind v2
on 02-02-2005 12:51 PM
Okay I've posted something on my blog regards that.

Hope a softie will bite :)

http://codebetter.com/blogs/sahil.malik/archive/2005/02/02/50343.aspx

fuul wrote re: More details on code behind v2
on 03-21-2005 4:13 PM

What the heck is this?

"...load the November CTP onto a machine (do not try this with the December CTP which has an older framework). "

Why would a later build have an older framework? If I am going to download one and start playing with it in general (not specifically doing what you were explaining) what version should I get??
Steve wrote re: More details on code behind v2
on 03-30-2005 8:25 AM
I agree with Paul above - this is a joke, I'm really disapointed with this move to html.

I picked .net since I could have code separation - but now it appears they have succumbed to the scripter world of web programming.

I don't mind options, but don't leave behind something that was working well.

note: MS - let developers work without touching html! Only time in 1.1 I touch html is to add javascript includes the rest is all code.
Craig Tadlock wrote re: More details on code behind v2
on 05-03-2005 12:48 PM
I don't see how this could be considered a move backwards. All they have really done is moved the generated code to a seperate file. This reduces human errors as well as making the designer's job a little easier. Nobody is forcing you to write code in the aspx page. This is exactly what partial classes are for.
lisa wrote re: More details on code behind v2
on 10-05-2006 5:38 AM
Great explanation -- not what I was looking for though... Trying to find sibling controls on the same page from within a control??

Add a Comment

(required)  
(optional)
(required)  
Remember Me?