Where the Code Lives





Where the Code Lives

The .NET Framework, as you may already know, is not an interpreted script platform. In the old days of Active Server Pages, Perl, and PHP, each page was interpreted line-by-line every time the page was executed. The script engine had to go through and figure out what every line was supposed to do every time.

In the .NET world, everything is compiled. Your page, as well as the C# or VB.NET code, is compiled at some point to Microsoft Intermediate Language (MSIL). MSIL is compiled just in time to code that the CPU understands. Assemblies are already in MSIL form, but your pages are not compiled until they're needed. Page compilation occurs once, and the resulting temporary assemblies are cached. That's the high-level view, but the actual text that makes up your code can come from a number of places. The following sections describe some of those places that the code can live. We'll show you the older code-behind models to be complete, but you should understand that with Visual Studio 2005 and ASP.NET v2.0, the inline and code-beside models are considered the two preferred schemes to handle page code. Classes intended for general use throughout the application either may be compiled to an assembly for the /bin folder, or the raw uncompiled class files can be placed in the /App_Code folder.

Inline Code

The closest thing to the old script days is the inline code model, where your methods appear in <script runat="server"></script> blocks, right in the .aspx page. Listing 6.1 shows an example of this technique.

Inline code

C#

<%@ Page Language="C#" %>
<script runat="server">
  void Page_Load(object sender, EventArgs e)
  {
    FirstLabel.Text = "Hello world!";
  }
</script>
<html>
<body>
  <form id="form1" runat="server">
    <asp:Label ID="FirstLabel" Runat="Server" />
  </form>
</body>
</html>


VB.NET

<%@ Page Language="VB" %>
<script runat="server">
  Sub Page_Load(sender As Object, e As EventArgs)
    FirstLabel.Text = "Hello world!"
  End Sub
</script>
<html>
<body>
  <form id="form1" runat="server">
    <asp:Label ID="FirstLabel" Runat="Server" />
  </form>
</body>
</html>

The first time this page loads, it is compiled into a class that resides in an assembly stored in a subfolder of the C:\WINDOWS\Microsoft.NET\ Framework\v2.x.xxxx\Temporary ASP.NET Files folder. That compiled version of the page is good until the .aspx file changes or the application is restarted, at which time it will have to be recompiled.

With ASP.NET v2.0, this way of writing code has come back into favor, provided it is being used solely for gluing together the UI and the classes that make up your business rules. This gets back into the application architecture we talked about in Chapter 4, "Application Architecture," where we separated the heavy lifting from the UI.

If you're tempted to drop script blocks all over the .aspx page, don't do it. One script block at the top of the page is all you should have. That block will have the typical set of event handlers, including Page_Load and event handlers that deal with button clicks, for example.

It's worth mentioning that your page inherits from the System.Web.UI.Page class, even though we haven't expressly indicated this. If you want the page to inherit from some other class (which must in turn eventually inherit from Page), you can indicate this by adding the Inherits attribute to the Page directive (you'll see this in the code-behind model).

Code-Behind: Non-Compiled

This technique of code separation was most commonly used by people who wanted to change code on-the-fly without having to "build" their projects, or by people who wanted to separate code from HTML but didn't have a tool like Visual Studio to automate the process. Generally speaking, using the inline or code-beside methods is more popular. Listing 6.2 shows a page using code-behind, without compiling the class.

Code-behind, non-compiled

default.aspx

<%@ Page Language="C#" Src="default.aspx.cs" Inherits="CodeBehindClass" %>
<html>
<body>
  <form id="form1" runat="server">
    <asp:Label ID="FirstLabel" Runat="Server" />
  </form>
</body>
</html>


C# (default.aspx.cs)

using System;
using System.Web;

public class CodeBehindClass : System.Web.UI.Page
{
  protected System.Web.UI.WebControls.Label FirstLabel;

  public void Page_Load(object sender, EventArgs e)
  {
    FirstLabel.Text = "Hello world!";
  }
}


VB.NET (default.aspx.vb)

Imports System
Imports System.Web

Public Class CodeBehindClass
  Inherits System.Web.UI.Page
  Protected FirstLabel As System.Web.UI.WebControls.Label 
  Public Sub Page_Load(sender As Object, e As EventArgs)
    FirstLabel.Text = "Hello world!"
  End Sub
End Class

This example uses two files (we've created two code-behind files, one for C# and one for VB.NET). The actual .aspx page looks just like the inline page, except that the code has been moved to the code-behind file. The first line, the Page directive, has two attributes that weren't there before. The Src attribute points to the physical file where the code-behind class is located (substitute this with default.aspx.vb if you're using the VB.NET version). The Inherits attribute tells us which class in that code-behind file the page should inherit because there technically could be any number of classes in that file.

The code-behind class must do two more things. First, it must inherit from System.Web.UI.Page, the base class for all ASP.NET pages. Second, it must declare all of the controls we'll access on the page as protected members of the class. That's why you'll notice that FirstLabel is declared in the structure of the class.

The first time this page is requested, it will be compiled and cached only as the inline code version, using the code-behind file as the source for its base class. It will remain cached until the .aspx or code-behind files have been changed or the application has been restarted.

Code-Behind: Compiled

For the developer, this works almost exactly like the non-compiled code-behind method. The important distinction is in the Page directive:

<%@ Page Language="C#" Codebehind="default.aspx.cs" Inherits="CodeBehindClass" %>

The Codebehind attribute was used only by Visual Studio 2002 and 2003 to let the designer know where the code was being kept. Because no Src was specified, the class indicated in the Inherits attribute had to be compiled in an assembly located in the /bin folder. In the older versions of Visual Studio, selecting Build -> Build Project from the menu would take all of the .cs (or .vb) files in the project and compile them to one assembly. The first time the page was requested, it would be compiled in much the same way as the previous methods, except that it would inherit from a class compiled in an assembly found in the /bin folder.

This scheme is obsolete and is not supported in Visual Studio 2005. It required constant synchronization between the pages and code files, generated a ton of code, and meant that you had to do a build every time you wanted to test the page. It's probably the reason that people new to the platform gave up on trying to use Visual Studio and went to WebMatrix, Dreamweaver, or even Notepad.

Code-Beside/Partial Classes

Although Visual Studio 2005 supports inline code better than any other development environment ever written, some developers might very well prefer to separate their code from the HTML in the .aspx file. There are advantages to doing this, including code reuse and the separation of design duties between a page designer and coder (anyone that hates designing HTML pages knows what I'm talking about). To accomplish this, we use partial classes, a new feature of the .NET Framework starting with v2.0.

Listing 6.3 shows the layout of the HTML in the .aspx page, as well as the separate code files. This is known as code-behind in the .NET documentation, but it is also frequently called code-beside.

Code-beside files

default.aspx

<%@ Page Language="C#" Codefile="default.aspx.cs"
  Inherits="Default_aspx" %>
<html>
<body>
  <form id="form1" runat="server">
    <asp:Label ID="FirstLabel" Runat="Server" />
  </form>
</body>
</html>


C#

using System;
using System.Web;

public partial class Default_aspx : System.Web.UI.Page
{
  public void Page_Load(object sender, EventArgs e)
  {
    FirstLabel.Text = "Hello world!";
  }
}


VB.NET

Imports System
Imports System.Web

Partial Class Default_aspx
  Inherits System.Web.UI.Page
  Public Sub Page_Load(sender As Object, e As EventArgs)
    FirstLabel.Text = "Hello world!"
  End Sub
End Class

Notice how much cleaner this is compared to the old code-behind model. Gone are the references to every control on the page, as well as all the generated code in the older versions of Visual Studio. When a request for the page is made for the first time, the page combines itself with the partial class to become the functional equivalent of the inline code example in Listing 6.1. Just as with the others, the compiled class remains valid until one of the involved files is changed.

Even setting up a new page in your Visual Studio 2005 project is easier because the new Web form dialog has a checkbox indicating you want to separate your code, as shown in Figure. If you check the box Place Code in Separate File, a partial class file will be generated for you.

5. Add New Item dialog for Web form.


The /App_Code Folder

There is one additional place you can put code, and given this book's emphasis on object-oriented programming, it's an important one. You can place class files (.cs or .vb) in the /App_Code folder of the application. Think of the /App_Code folder as an uncompiled /bin. Class files stored here are compiled when the application is run, and they can be accessed from any page or class in the application. This is especially handy in the Visual Web Developer 2005 Express Edition because that product doesn't "build" (compile all class files to an assembly) the way that Visual Studio does, and you can't reference a separate class library project.

The development environment is smart enough to know where to place code. For example, if you added a LinkButton to the page in Listing 6.1 using the visual designer, double-clicking that control will create an event handler method inline, in the script block. If you did the same thing to our example in Listing 6.3, the event handler method would be added to the code-beside file.

To be complete, we should mention that there is another place that a compiled class can be reached other than the /bin folder of the Web application. The global assembly cache (GAC) is a collection of classes that can be accessed by any .NET application on the computer. The command line utility gacutil.exe, found in Visual Studio's SDK\bin folder, is used to add the classes found in an assembly to the GAC. This might be useful to you if there are classes that several applications on the box need to access without having to maintain separate copies of them for each application. Keep in mind, though, that the design of .NET is such that it should eliminate "DLL hell," the phenomenon from the COM days where several versions of a library existed on the same computer, causing confusion for consuming applications. With this in mind, it's a good idea to deploy assemblies with the applications they were meant for.

You can get more information on using the GAC here:

http://www.gotdotnet.com/team/clr/when_to_use_the_gac.aspx


A fairly common technique is to build a class library outside of the site project itself, where you keep all of your business objects and do the "heavy lifting." By adding a class library project to a solution in Visual Studio 2005, you create some of the code separation we talked about in Chapter 4.

You'll need to do two things to use a class library in your solution. First, right-click the Web project in the solution explorer and choose Add Reference. On the Projects tab of the resulting dialog, choose the project in the solution that contains your class library. Visual Studio will automatically copy the compiled assembly of that project into the /bin folder of the Web project whenever it's changed.

The second thing you'll need to do in your class library is to create references to ASP.NET's objects. In addition to importing (using in C#) the System.Web namespace, you'll need to get some kind of context to the application and page's lifecycle. You can do this by creating a new HttpContext object and assigning HttpContext.Current to it. In your code, typing the name of the object followed by a period will cause Intellisense to pop up all of the familiar objects you'd normally find when coding in the page, such as Request, Response, Cache, and so on. You got to see this technique in action in Chapter 5, "Object-Oriented Programming Applied: A Custom Data Class," when we added caching to our custom data class.


     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows