Model-View-Controller and ASP.NET

This article was originally published in VSJ, which is now part of Developer Fusion.
Microsoft introduced ASP back in December 1996, and it was quickly pounced on by many web developers as a great way of developing web applications. However, problems dogged ASP development over the years, in particular developers had to mix content and logic on the same page. This led to many issues: it makes pages hard to read; it makes logic hard to re-use; it can lead to performance issues; it makes code hard to test; and it’s hard to debug.

In July 2000 Microsoft introduced the .NET framework and with it ASP.NET. ASP.NET set out to fix some of the above problems with ASP, in particular (as far as this discussion goes) Microsoft wanted to separate the page layout from the page logic. To try and ensure this separation Microsoft introduced the ‘code-behind’ development model. In code-behind, each ASPX page has an associated class; this class contains code that can be used to manipulate the page and to change the content and layout of the page.

While the code-behind model solves some of the issues mentioned above it does not solve them all. There are still issues with having the same logic embedded into many ‘pages’ and there is also the issue of having multiple views of the same data. Take the example of a Web Log (or Blog): it has at least two display modes, HTML and XML, so a Blog has different views of the same data. Managing multiple views using the code-behind model can be done, but it often leads to duplicated effort, for example each code-behind class would need to read the database to access the data before passing the data to the page to be displayed.

There are ways of partitioning the application so that each concern (the user interface “concern” and the business logic “concern”) can be dealt with separately. We will address one approach here, the so-called Model-View-Controller or MVC idiom. In fact there are multiple ways of writing an MVC application, often called the ‘front controller’ and the ‘page controller’ approaches; again we will cover both but concentrate on the front controller. Before we talk about these different approaches, however, we need to discuss what MVC actually means.

Prior to diving into the depths of MVC we need to mention one final thing. The ultimate aim of MVC is to remove all code from a page (in this case an ASP.NET page), however that is impractical, if not impossible. Therefore our aim will be to remove all business logic code from the page, as there is still a need for what I like to call ‘view logic’ (that is, code that is used to modify the user interface, such as setting the correct data on the page). Note that in other environments such as JSP, the view programming language would be distinct from the business logic language, and would use some sort of template system, whereas in .NET the view programming language has to be one of the .NET family, which typically means C# or VB.NET.

Model-View-Controller

‘Back in the days’ when computing as we know it was in its infancy, a group at Xerox’s Palo Alto Research Centre (PARC) created many of the things that we take for granted today. This includes Ethernet; mice; bit-mapped displays and graphical user interfaces. The same group also developed Smalltalk, and one of the things they realised during this work was that it was possible to display the same data in multiple ways. Because of this the Smalltalk team realised they needed to keep separate the view code from the code that generated the data.

For example, in a spreadsheet program a given data set may be displayed as a table (a spreadsheet), or as one of many types of graph, or in any other way you choose. This led to the idea of separating the data to be displayed (called the ‘model’) from the display mechanism (called the ‘view’), and to the realisation that a given model may have multiple distinct views. However, if you have multiple views per model something is need to manage the interaction between the views and the models, i.e. to mange the connection between the view and the model and to notify the view when the model changes. This final piece is called the ‘controller’, hence the Model-View-Controller, or MVC idiom was defined.

MVC in web applications

The approach taken in web applications is not quite the same as the original MVC approach, but it is close enough to mean that people use the term MVC quite freely. In MVC the controller notifies the view that the model has changed by firing ‘events’; this doesn’t happen in web applications, as the view and model are physically separate (the view is on the user’s machine, while the model is on the server).

So what make up the parts of MVC?

In MVC the view is (I hope) obvious, ASPs or in our world ASPX pages, but what about the model and the controller?

The model is code that will execute business logic and hold the data for the view to display. This code can be any object loaded from an assembly, i.e. any C#, VB.NET or other .NET objects will do.

Be aware that, as well as having multiple views per model within one application, another reason for separating the model from the view is to allow the model to be re-used in other applications, e.g. re-using a model from a web application in a Windows Forms application. This means that the classes used for the model should be as “domain independent” as possible. The classes that make up the model should not contain references to domain-specific types such as anything from the System.Web namespace, e.g. Request or HttpSessionState. In Java these types are called Plain Old Java Objects or POJOs, so maybe in .NET we should call them Plain Old DotNet Objects or PODOs!

So there is a model and there are views, how do we bring the model and a view together? That is the job of the controller. The controller has to do two things – identify the business logic to execute, and identify the view to use to display the results of executing the business logic. To do this the view has to see the request that is sent into the application, i.e. the requests do not go directly to an ASPX page, but instead get passed to a controller which then causes any necessary logic to execute, and finally passes the request to the appropriate view component.

In ASP.NET there are two possibilities for implementing the controller component. It can be implemented as either a Module or a Handler. Modules are classes that implement the IHttpModule interface and handlers implement IHttpHandler.

System.Web.Page is a handler, which means that all pages are handlers. Handlers are typically associated with a specific extension and perform a specific task. ASP.Net comes with a number of built in handlers, as a look at machine.config will confirm. For example, there is the System.Web.HttpForbiddenHandler that disallows access to files with specific extensions and System.Web.StaticFileHandler for processing files that exist on disk such as HTML files.

Modules on the other hand can be thought of as filters. A module can get to see every request, not just requests for specific URLs, and it can modify the request and response data. A module can register event handlers to deal with specific events that occur during the lifetime of a given request. Again, ASP.NET comes with many pre-configured modules such as System.Web.Security.WindowsAuthenticationModule and System.Web.Security.FormsAuthenticationModule, which should give you a clue as to how modules are typically used (as they see every request, they are an excellent way to manage authentication and authorization within a web application).

We will use a module in this example.

MVC in action

When using the MVC idiom the general idea is to intercept all requests to the server, process the request based on the URL and query string, execute the business logic for request and then forward to the appropriate page.

Figure 1: The MVC structure
Figure 1: The MVC structure

Figure 1 shows a request coming in from a browser; that request being handled by the controller; the controller, based on the URL, decides which objects to create and then “executes” those objects; once the business logic has executed, the module stores the PODOs in a place where the ASPX page can access them; based on the URL the module then passes the request on the appropriate ASPX page; finally the ASPX renders the response using the data stored by the module.

Notice that we are still using .aspx pages to render the response. This makes sense as it means that the full range of ASPX features is available such as server-side controls and data-binding. The controller gets the request to the ASPX by using the HttpContext’s RewritePath method, we will cover this in detail in a moment.

This means requests do not go to the .aspx pages directly, so if we are not using .aspx as the extension what should we use? A common extension in an MVC scenario is “.do”, i.e. “do” the action I’m requesting. Thus our module will need to handle all requests and process those that end in .do. The request processing will execute the business logic then rewrite the request so that it passes to the ASPX page that we want to use to display the data. (I would strongly recommend reading Ian Griffith’s weblog entries here and here where Ian discusses how to use ASPX without any extensions.)

Remember this means that the request does not go straight to the ASPX page but instead goes to the module, and in fact we don’t want requests to ever go to the page without first going to the module. This means that we must have the application reject all requests that end in .aspx.

So how do we stop requests to .aspx pages? An initial thought might be to configure an HttpForbiddenHandler in our web.config to stop access to APSX pages, unfortunately this doesn’t work. Well it does work and that’s the problem. If, in our controller, we ‘rewrite’ the request so that it forwards to a .aspx page then the HttpForbiddenHandler stops the rewrite working. This means that in our module we need to process requests that end in .do and reject those that end in .aspx or other ‘forbidden’ extensions.

If we are using .do as the extension we also need to configure .do so that it is processed in our application by ASP.NET. This has to be done through the IIS configuration. You do this through the properties page for the web site (see Figure 2), click on ‘Configuration’ (see Figure 3) and then add the .do extension (see Figure 4).

Figure 2: The properties page
Figure 2: The properties page

Figure 3: Click on 'Configuration'
Figure 3: Click on ‘Configuration’

Figure 4: Add the .do extension
Figure 4: Add the .do extension

Note that the executable is C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\aspnet_isapi.dll and that the ‘Verify that file exists’ checkbox is unchecked as there is no file ending in the extension .do.

Writing the controller

We decided above to use a module as the controller. What does a module look like? To be a module a class must implement the IHttpModule interface and be configured as a module within the application. The interface looks like this:
public interface IHttpModule
{
	void Dispose();
	void Init(HttpApplication context);
}
Notice that the module’s lifetime is defined by two methods, Init and Dispose, these are called when the module is loaded and unloaded respectively. The Init method is passed an HttpApplication instance and this is used to register appropriate event handlers with the runtime.

HttpApplication has some fourteen events available that a given module can handle, they range from BeginRequest, through AcquireRequestState to EndRequest.

To be a controller we have to intercept requests at the correct point in the ASP.NET processing model by responding to the appropriate event. The obvious event to choose seems to be BeginRequest and this event works fine unless you are using ASP.NET form based authentication. Remember that the controller will process the incoming request by doing some work then re-writing the request so that it gets sent to the appropriate page. If you want to use form-based security on the URL that the user requests, rather than the URL that the controller rewrites, you have to use the AuthorizeRequest event rather than the BeginRequest event (AuthorizeRequest is raised after the request has been authorized – again I would strongly recommend Ian Griffiths’ web log entries I referenced previously for more details on this).

This means our module initially looks like this:

using System;
using System.Web;
namespace simpleblog
{
	public class Controller :
		IHttpModule
	{
		public void Init(
			HttpApplication context)
		{
			context.AuthorizeRequest +=
				new EventHandler(
				OnAuthorizeRequest);
		}
		private void
			OnAuthorizeRequest(
			object sender, EventArgs e)
		{
			HttpApplication application=
				(HttpApplication)sender;
		}
		public void Dispose()
		{
		}
	}
}
We also need to configure the web.config so that the module is registered. The web.config will look like this:
<configuration>
	<system.web>
		<compilation
			defaultLanguage=”c#”
			debug=”true” />
			<httpModules>
				<add name=”Controller”
			type=”simpleblog.Controller,
					simpleblog” />
			</httpModules>
			<authentication mode=”Forms”/>
	</system.web>
</configuration>
That is the basics of the module setup. Each request that comes in will pass to the event handler. The handler now has to process the events, reject those that are invalid, process those that it knows about and leave everything else alone. To do this the handler has to check the extension of the incoming request (if it has one) and process the request based on this extension.

The first thing to do is to check that the URL ends with .do and if it does to perform the specific action (which we will come to in a moment). If the URL does not end in .do then we should redirect the request to the appropriate action. Redirect rather than re-write because we want the correct URL to show in the browser for bookmarking purposes.

if(absolutePath.EndsWith(“.do”))
{
// get name of the action from the URL
// http://some.server/view.do
// gives ‘view’ as the action
	int ndxStart =
	absolutePath.LastIndexOf(“/”) + 1;
	int length =
		absolutePath.LastIndexOf(“.”)
		- ndxStart - 1;
	string action =
		absolutePath.Substring(
		ndxStart, length);
// execute the action
}
else
{
	application.Response.Redirect(
		“~/view.do”);
}

Actions

The controller now has its outline functionality and we need to add the code to process the particular action being requested and then forward to the appropriate view. As always we have several choices here, for example we could write in-line code, i.e. place a switch statement inside the event handler and perform the actions there, something like:
switch(actionName)
{
case “view”:
	// do all the view work here
	break;
case “viewrss”:
	// do the viewrss work here
	break;
	// etc.
}
However, as you can imagine this quickly gets tedious and uncontrollable. Instead we will factor the action code into different classes that we will call ‘Actions’.

Action classes are not the business rules that are being executed, instead they can be thought of as part of the controller code but factored out into separate classes. We will decide which action to execute based on the URL. There can be many actions such as ‘view’, ‘edit’, ‘delete’ etc., and obviously the actions depend on the application that is being authored. In this case we will be writing a simple Web Log (Blog) that will have two actions, view and viewrss. (RSS (Real Simple Syndication or Resource Site Summary or…) is an XML grammar for Blogs. It allows Blogs to be read by clients other than browsers. These clients are called “News Aggregators”. A search will turn up many examples of aggregators for different platforms.)

Actions will typically look structurally very similar, so it is usual to create a base class that all actions extend. The base class will have a single method called Execute that all subclasses must implement. In this application we will call that class BaseAction and have each action class extend it, something like:

namespace simpleblog
{
	abstract virtual public class
		BaseAction
	{
		abstract public bool
			Execute(HttpContext ctx);
	}
}
namespace simpleblog
{
	public class ViewAction :
		BaseAction
	{
		public override bool
			Execute(HttpContext ctx)
		{
			// execute business logic
		}
	}
}
The Execute method accepts a reference to the current HttpContext. We said above that business logic should be domain independent and here we are passing HttpContext to the actions. But remember that actions are not part of the model; they are part of the controller.

As this is a simple example the Execute returns a bool signalling success or failure, in real world code you may want to return some more detail information such as a collection of errors and maybe throw an exception. We now need to setup the controller to use these actions. One way to do this is to create an ‘associative array,’ that is a collection that maps action names to action classes. We can do this in the controller’s constructor:

private Hashtable actions =
	new Hashtable();
private const string DEFAULT =
	“default”;
public Controller()
{
	actions.Add(DEFAULT,
		new ViewAction());
	actions.Add(“view”,
		new ViewAction());
	actions.Add(“viewrss”,
		new ViewRSSAction());
}
We can then modify the event handler code to execute the appropriate action:
if(absolutePath.EndsWith(“.do”))
{
	int ndxStart =
	absolutePath.LastIndexOf(“/”) + 1;
	int length =
	absolutePath.LastIndexOf(“.”)
		- ndxStart - 1;
	string actionName =
		absolutePath.Substring(
		ndxStart, length);
	action = (BaseAction)actions[
		actionName];
}
else
{
	application.Response.Redirect(
		“~/view.do”);
}
if(action == null)
{
	action = (BaseAction)actions[
		DEFAULT];
}
action.Execute(application.Context);
Notice that there is a default action that is used if the specified URL does not match any known action.

The job of the action is to create the PODOs to execute and contain the results of the business logic. The business logic of the view action is simply to retrieve the set of ‘blog’ entries to display. The entries are stored in a SQL Server database. To do this we will create a class (BlogEntries) to execute the SQL to retrieve the entries that should be displayed. Each entry from the database will be stored in another class (BlogEntry) and BlogEntries will return a collection of BlogEntry instances (it’s easier to code than it is to read!).

The ViewAction Execute method looks like this:

public override bool
	Execute(HttpContext ctx)
{
	ArrayList allEntries = null;
	BlogEntries entries =
		new BlogEntries();
	using (IDbConnection conn = new
	SqlConnection(ConfigurationSettings.
		AppSettings[“dsn”]))
	{
		ArrayList allentries = entries.
			RetrieveBlogEntries(conn);
	}
	ctx.Items[“entries”] = allEntries;
	ctx.RewritePath(“~/view.aspx”);
	return true;
}
This method creates a database connection and then calls the BlogEntries’ RetrieveBlogEnties() method which we will see in a moment. We then store the returned ArrayList in the HttpContext’s Items collection and forward to the view page. The page can then retrieve these entries and display them on the page.

Notice that the Action gets the database connection. This is done in the action and not in the BlogEntries class because we want the BlogEntries class to be domain independent, i.e. we don’t want the BlogEntries class getting the DSN from web.config. Another option would be to pass the DSN string and have the BlogEntries class connect itself using this connection string.

The connection string itself is stored in the web.config like this:

<configuration>
	<appSettings>
		<add key=”dsn” value=”server=.;
			database=simpleblog;
			user=simple;pwd=foobar”/>
	</appSettings>
	...
</configuration>
The BlogEntries class iterates over the table in the database and creates one BlogEntry for each row. It stores each BlogEntry in an ArrayList and returns the ArrayList to the action.

In this example the BlogEntries and BlogEntry classes are the model. Notice that the model is reasonably simple; notice also that the PODO that executes the logic (BlogEntries) does not store any data. Instead the data is stored as BlogEntry instances in an ArrayList. Again, this is a standard approach, the data and the logic to collect that data do not necessarily go together.

The view

Finally the view. The controller forwards to the view by calling HttpContext.RewritePath(). Before doing this the controller has to store any data that the view needs. That data can be stored in the HttpContext.Items collection, in the SessionState collection, in the HttpApplicationState or anywhere else the View has easy access to it. The job of the view is to format the data into a consumable format, this could be XML, HTML or some other format such as PDF, and it could be consumable by humans, for example through a browser, or by machines, for example through SOAP. In this case we have two views, one is HTML, and the other is an XML dialect called RSS.

Looking at the HTML view, the page is called view.aspx and the code-behind file is view.aspx.cs. The ASPX file looks like this:

<%@ Page language=”c#”
	Codebehind=”View.aspx.cs”
	AutoEventWireup=”false”
	Inherits=”simpleblog.View” %>
	<body>
		<div id=”banner”>
		<%
		Response.Write(
	ConfigurationSettings.AppSettings[
			“weblog.title”]);
		%>
		<br>
	<span class=”bannerdescription”>
		<%
		Response.Write(
	ConfigurationSettings.AppSettings[
			“weblog.description”]);
		%>
		</span>
		<br>
		</div>
	<asp:table id=”_blogEntriesTable”
		Runat=”server”
		BorderStyle=”None”
		cellspacing=”0” cellpadding=”0”
		width=”100%”
		EnableViewState=”false” />
</body>
There are two code blocks that simply display data from the web.config file as the blog title and the description. There is also an asp:Table tag, this is used as a place holder and is populated in the code-behind class. The code-behind class looks like this:
protected Table _blogEntriesTable;
private void Page_Load(object sender,
	System.EventArgs e)
{
// Put user code to initialize the
// page here
	DateTime currentDate =
		new DateTime(0);
	foreach (BlogEntry entry in
(ArrayList) HttpContext.Current.Items[
		“entries”])
	{
// create a container for all the divs
// the output will look like
// <div>
//	<div class=”date”>Wednesday 1 Jan
//		2003</div>
//	<div class=”blogtitle”>title1</div>
//	<div class=”blogbody”>entry1</div>
// </div>

		Panel container = new Panel();

// only display date for first posting
// that day
		if (currentDate != entry.Date)
		{
			currentDate = entry.Date;
// add date as a sub panel

		AddDivToPanel(
			entry.Date.ToString(
			“dddd d MMM yyyy”),
			“date”, container);
		}

		AddDivToPanel(string.Format(
		“<a name=’{0}’><b>{1}</b></a>”,
			entry.BlogId, entry.Title),
			“blogtitle”, container);
		AddDivToPanel(entry.Entry,
			“blogbody”, container);

// some code elided, see project code
// for details

		TableRow row = new TableRow();
		TableCell cell =
			new TableCell();
		cell.Controls.Add(container);
		row.Cells.Add(cell);
		blogEntriesTable.Rows.Add(row);
		}
	}
}

private static void AddDivToPanel(
		string entry, string cssClass,
		Panel container)
{
	Panel div = new Panel();
	div.CssClass = cssClass;
	LiteralControl text =
		new LiteralControl();
	text.Text = entry;
	div.Controls.Add(text);
	container.Controls.Add(div);
}
This code uses the WebForms model and creates various controls to represent HTML output, notably it uses TableRow, TableCell and Panel to model <tr>, <td> and <div> respectively.

The RSS view uses exactly the same data as the HTML view but uses a different technique to display the data. The ASPX page looks like this:

<%@ Page CodeBehind=”ViewRss.aspx.cs”
	Language=”c#”
	AutoEventWireup=”false”
	Inherits=”simpleblog.ViewRss”
	contentType=”text/xml” %>
<asp:Repeater id=”_rss”
	runat=”server”>
	<HeaderTemplate>
		<rss version=”2.0”>
			<channel>
				<title>
	<%# GetTitle(Container.DataItem) %>
				</title>
<link>”http://server/goes/here”</link>
				<description>
<%# GetDescription(Container.DataItem) %>
				</description>
	</HeaderTemplate>
	<ItemTemplate>
		<item>
			<title>
				<%# DataBinder.Eval(
		Container.DataItem, “Title”) %>
			</title>
			<description>
		<%# DataBinder.Eval(
		Container.DataItem, “Entry”) %>
			</description>
			<link>
http://www.krjkrjkrj.com/Story.aspx?ID
=<%#
				DataBinder.Eval(
				Container.DataItem,
				“BlogId”) %>
			</link>
			<author>Kevin Jones</author>
			<pubDate>
			<%# String.Format(“{0:R}”,
	DataBinder.Eval(Container.DataItem,
				“Date”)) %>
			</pubDate>
		</item>
	</ItemTemplate>
	<FooterTemplate>
		</channel>
		</rss>
	</FooterTemplate>
</asp:Repeater>
Notice that this page uses the repeater tag to iterate over a DataSet; that DataSet is simply the ArrayList returned by the Model. The code behind this page sets up the Repeater’s data set and implements the GetTitle and GetDescription methods shown above to return the data stored in the web.config:
protected Repeater _rss;
private void Page_Load(object sender,
	System.EventArgs e)
{
// Put user code to initialize the
// page here
	ArrayList entries = (ArrayList)
		HttpContext.Current.Items[
		“entries”];
	_rss.DataSource = entries;
	_rss.DataBind();
}
protected string GetTitle(object data)
{
	return ConfigurationSettings.
		AppSettings[“weblog.title”];
}
protected string GetDescription(
	object data)
{
	return ConfigurationSettings.
		AppSettings[
		“weblog.description”];
}
Notice that neither view implementation relies on any sort of data access, and in fact the RSS view simply relies on some form of iteration, it doesn’t care what the data type is or where that data comes from, it could be a database or, as in this case, a collection class of some sort. This makes the view highly decoupled from the model.

Benefits of MVC

The primary benefit of a Model 2 design is the ‘separation of concerns’. Each part of application does what it does best and each part is easy to extend. As we have seen in even this simple example, separating the concerns has led to two major benefits, the first of which is code re-use. The same model can be applied to multiple views. Secondly, you can get a high degree of de-coupling. De-coupling has many benefits – for example it allows for much easier re-use and re-factoring of the code. There are also other benefits, for example testing the business logic becomes much easier if the logic is placed into separate .NET classes and assemblies (the PODOs used above), rather than if the logic is embedded into the ASPX pages.

Notice however that there is a lot more work to be done to implement the MVC pattern and this extra work is not always warranted.

Other issues

There are certain other things to note here:
  • The mapping between the action name and the action to perform is hard-coded
  • The mapping between the action and the view is hard-coded (i.e. each action ‘rewrites’ to a specific view)
  • The actions all use Rewrite, suppose they sometimes want to re-direct?
  • Suppose one action sometimes wants to be able to execute other actions
These are all essentially configuration issues and are reasonably easy to solve. Rather than hard-coding the configuration data it is often better to store that data in a separate file. In ASP.NET, the web.config file can be used. In this case it would be necessary to write a configuration handler to process the data and then wire up the actions and the views, and this is a common approach in other frameworks (see for example the Struts framework at struts.apache.org).


Kevin Jones is an instructor and course author who teaches both Java and .NET courses for DevelopMentor. He is a frequent speaker at industry events, and is the co-author of Servlets and JavaServer Pages: The J2EE Technology Web Tier, published by Addison-Wesley.

You might also like...

Comments

About the author

Kevin Jones United Kingdom

Kevin Jones has been involved in software development and design for more years than he cares to remember, and has been involved in training and consultancy since 1990. He is the co-author of Se...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“If debugging is the process of removing software bugs, then programming must be the process of putting them in.” - Edsger Dijkstra