Global.asax Using Codefile: Good Practice or Madness?

I’d like to convince you that using a code file for the Global.asax file is a good idea in ASP.NET web application projects.

Visual Studio supports two web deployment models: a web site or a web application. A web site is basically a folder of files. You must deploy all the files in the project in order for the project to work correctly. A web application is a traditional C#/VB project (i.e. a Class Library) optimized for ASP.NET. You must compile the entire site before deployment. Only the client-side content, markup code, and compiled assemblies are necessary for the project to work correctly.

There is a great article on the Microsoft MSDN web site that describes the differences in much more detail:

http://msdn.microsoft.com/en-us/library/dd547590(v=vs.110).aspx

The Global.asax file, is a special file within either an ASP.NET web site or web application project.

The Global.asax file, also known as the ASP.NET application file, is an optional file that contains code for responding to application-level and session-level events raised by ASP.NET or by HTTP modules. The Global.asax file resides in the root directory of an ASP.NET application. At run time, Global.asax is parsed and compiled into a dynamically generated .NET Framework class derived from the HttpApplication base class. ASP.NET is configured so that any direct URL request for the Global.asax file is automatically rejected; external users cannot download or view the code in it.

The Global.asax file is optional. You create it only if you want to handle application or session events.

Source: http://msdn.microsoft.com/en-us/library/2027ewzw(v=vs.100).aspx

This file started its life as the Global.asa file in ASP (i.e. ‘Classic ASP’). It originally served several purposes:

  • Application-level event handlers
  • Session-level event handlers
  • Object declarations
  • Type Library references
  • Include ‘server-side include’ directives

So when Microsoft build ASP.NET to replace Classic ASP, it retained the concept using the Global.asax file (because having file extensions ending in an ‘x’ is just cool, right?).

In a web site project template, Visual Studio creates a single file (Global.asax) and uses markup syntax. Here is an example:

<%@ Application Language="C#" %>
<%@ Import Namespace="MyWebSite" %>
<%@ Import Namespace="System.Web.Optimization" %>
<%@ Import Namespace="System.Web.Routing" %>

<script runat="server">

    void Application_Start(object sender, EventArgs e)
    {
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }

</script>

Refer to the MSDN web site for more details on this syntax: http://msdn.microsoft.com/en-us/library/2027ewzw(v=vs.100).aspx

When Visual Studio introduced the web application project concept, it modified the Global.asax file template to match the ‘standard’ syntax it used for all Web Form files (e.g. ASPX files). It created a code-behind file, called Global.asax.cs (or Global.asax.vb if using Visual Basic) and moved all code from the markup file into the code file. This is what we see today using the built-in MVC web application projects.

Global.asax

<%@ Application Codebehind="Global.asax.cs" Inherits="MyWebSite.MvcApplication" Language="C#" %>

Global.asax.cs

using System;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace MyWebSite
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}

Visual Studio compiles the code in the Global.asax code-behind file into the project assembly so all you need to do is deploy the Global.asax markup file. And here is where my problem lies.

The ASP.NET application file is pretty special. It is where a web application can control a great deal of functionality. First and foremost, it provides one of the most widely used means of initializing a web project at startup (via the Application_Start event handler).

As a side note, many third party libraries now use NuGet to install themselves into an ASP.NET project. Several of them have started to use WebActivator to incorporate themselves into the startup pipeline. I don’t see this approach as a good idea in all situations, since you now have multiple startup injection points. In such cases, it is even more difficult to see/understand an application’s startup processing pipeline once deployed.

Another popular use of the Global.asax file, is the interception of unhandled exceptions (via the Application_Error event handler) and provide logic to each one encountered by the application (like logging the exception details). In fact, any of the events exposed by the HttpApplication class are accessible in the Global.asax. A popular event I see in use is the AuthenticateRequest event because it allows the application to control how request authentication occurs. The file also provides access to session-level events (Session_Start and Session_End) if your site uses Session state.

In addition to the built-in ASP.NET bundling and routing setup, many third-party libraries hook into the Application_Start event handler to initialize their services. For example, many dependency injection frameworks initialize their mappings via the application startup process.

As you can hopefully see, the Global.asax frequently contains code for many ‘cross-cutting concerns’. Since this file is the main entry point for the application, it is useful to know the contents of the file. You don’t always care to know how an application works, but seeing the initialization steps can be pretty helpful, as well as seeing the application/session event handlers.

Using the original web site project syntax, this isn’t a problem, since the markup file contains all the logic. However, in the web application project style, once deployed, you cannot see the logic contained in the code-behind file.

So what is a developer to do? I offer that if you could see the contents of the Global.asax.cs code-behind file in the deployed code, you would be better able to support the application once deployed. Thankfully, ASP.NET provides a way to do this, with only a few small changes to the existing files.

Step 1: Open the Global.asax markup file (right-mouse click on the file and select ‘View Markup’)

viewmarkup

In the Application directive, change the attribute from Codebehind to CodeFile:

<%@ Application CodeFile="Global.asax.cs" Inherits="MyWebSite.MvcApplication" Language="C#" %>

Step 2: Open the Global.aspx.cs file and add the partial modifier to the class declaration:

public partial class MvcApplication : System.Web.HttpApplication

Step 3: In the solution explorer, select the Global.asax file and view its properties (click Alt+Enter or right-mouse click and select ‘View Properties’). You need to change the Build Action from Compile to Content.

globalasaxproperties

Done!

Changing from Codebehind to CodeFile let’s the run-time environment know what code to run. Codebehind looks for the code in the compiled web application assembly. CodeFile looks for the actual file and compiles the logic at run-time.

The partial class modifier is necessary. Just because. Go with it.

Changing the build action from Compile to Content lets the publish process know that the file needs to go along with all the other content files. Without the Global.asax.cs file, it won’t work!

A couple of parting thoughts. First, this approach of deploying a code file normally compiled into the web application assembly can be useful in a number of other situations. For example, you could apply this process to any code files in an MVC project by placing them in the ASP.NET App_Code folder. If you are using code to configure a Dependency Injection library (like Autofac), you might want to deploy the code source file in order to see the configuration information at run-time.

Lastly, if you deploy the source files, it does give you the option to modify the files once deployed. This might prove very useful when a change to a cross cutting concern (like exception handling or logging or authentication) is necessary.