ASP.NET MVC Framework

Exclusive offer: get 50% off this eBook here
ASP.NET 3.5 Application Architecture and Design

ASP.NET 3.5 Application Architecture and Design — Save 50%

Build robust, scalable ASP.NET applications quickly and easily.

$23.99    $12.00
by Vivek Thakur | November 2008 | .NET Architecture & Analysis Microsoft

These days, Model View Controller (MVC) is a buzzword in the ASP.NET community, thanks to the upcoming ASP.NET MVC Framework that Microsoft is expected to launch soon. The Framework allows easier adoption of the different MVC patterns in our web applications.In this article by Vivek Thakur, we discuss ASP.NET MVC Framework in detail with the help of a Sample Project. We also take a glance at Unit Testing with reference to ASP.NET MVC Framework.

The ASP.NET MVC framework was released by Microsoft as an alternative approach to webforms when creating ASP.NET based web applications. The ASP.NET MVC framework is not a replacement or upgrade of webforms, but merely another way of programming your web applications so that we can get the benefits of an MVC design with much less effort.

(For more resources on .NET, see here.)

As of now, the ASP.NET MVC framework is still in CTP (Community Technology Preview, which is similar to an advanced pre-stage), and there is no certain date when it will be released. But even with the CTP 5, we can see how it will help MVC applications follow a stricter architecture. We will quickly see how to use the ASP.NET MVC framework through a small example.

Sample Project

First, download the ASP.NET MVC framework from the Microsoft website and install it. This installation will create an MVC project template in VS 2008.

Start VS 2008, select the File | New Project menu item and then choose theASP.NET MVC Web Application template to create a new web application using this template.

ASP.NET MVC Framework

There are many free unit testing frameworks available for ASP.NET projects, and NUnit and MBUnit are two of the most popular ones. Here are the links:

MBUnit: http://www.mbunit.com/
NUnit:; http://www.nunit.org/index.php

ASP.NET MVC Framework

Select the default option and click OK. You will notice that two projects have been added to the solution that VS has created. The first project is a web project where you'll implement your application. The second is a testing project that you can use to write unit tests against.

ASP.NET MVC Framework

In our custom MVC code project, we had different projects (class libraries) for the model, the view, and the controllers. The default directory structure of an ASP.NET MVC Application has three top-level directories:

  • /Controllers
  • /Models
  • /Views

When the project becomes large, it is recommended that the Model, Views and Controllers are put in separate class library projects of their own so that it's easy to maintain them. But for the purpose of illustrating the ASP.NET MVC framework, this default structure is fine for us.

We will create a simple customer management application. For this, we first create some ASPX pages in the Views folder. Note that VS has already created these subfolders for us, under Views:

  • Home: Contains the and Index views
  • Shared: Contains shared views such as master pages

Before we go on to adding custom code in this project, let us understand what VS has done for us while creating this MVC project.

URL Routing Engine

In the standard ASP.NET model (or Postback model), the URLs map directly to the physical files:

ASP.NET MVC Framework

So when we make a request to a page, say MyPage.aspx, the runtime compiles that page and returns the generated HTML back to IIS to be displayed by the client browser. So we have a one-to-one relationship between the application URLs and the page.

But in the MVC framework, the URLs map to the controller classes.

ASP.NET MVC Framework

Therefore, the URL is sent to IIS and then to ASP.NET runtime, where it initiates a controller class based on the URL, using the URL routes, and the controller class then loads the data from the model, with this data finally being rendered in the view. The controller classes uses URL routing to map the URLs, which in simpler terms means rewriting URL. We can set up the rules for which URL is to be routed to which controller class. The routing will pick up the appropriate controller and pass in the query string variables as necessary. Open the global.asax.cs file and examine the following code:

public class GlobalApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" }// Parameter defaults
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}

The RegisterRoutes() method contains the URL mapping routes. Initially we have only the default rule set:

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);

The RegisterRoutes() method contains the URL mapping routes. Initially we have only the default rule set:

The MapRoute() method, which handles URL routing and mapping, takes three arguments:

  1. Name of the route (string)
  2. URL format (string)
  3. Default settings (object type)

In our case, we named the first route "Default" (which is the route name) and then set the URL as:

Controller/action/id

The Controller here is the name of the controller class. action will be the method that needs to be invoked inside that controller class. id would be the parameters that need to be passed, if any.

In the default arguments, we create a new object and call it "Home", set the action to Index, and do not pass parameters to it. Note the new anonymous type syntax used to create parameter defaults:

new { controller = "Home", action = "Index", id = "" }

The var keyword and anonymous types: We normally use classes to wrap behavior and properties, but in C# 3.0, we can create the types anonymously without needing to create classes for them. This can be useful when we need to create light weight classes that have only read-only properties. We can use the anonymous syntax to create those types without the need to create a class for them. We can use the new "var" keyword to hold such anonymous types, for example: var ch = new { readOnlyProperty1 = value1, readOnlyProperty2 = value2 };

It is important that we name and assign a value to each of the properties that we are creating. What will be the type of the properties? They will automatically be cast to the data types of the values of the properties specified. The anonymous types will always be derived from the base object class directly. They can only be used within class members and cannot be passed as method arguments (unless they are boxed), return values, or be specified as class-level variables. Once the type is created, it cannot be changed into another type.

So we create a new anonymous type as the last argument of the MapRoute() method, passing in variable defaults with three properties, namely controller, action, and parameter.

Now have the Default.aspx page under the root directory, which acts as a redirecting page to the main home page of the site (which is /View/Home/Index.aspx). We cannot directly set that as the "default" page since we are using URL routes to process pages instead of using physical files in the URLs. So in the code-behind of our Default.aspx page, we have a simple redirect:

public void Page_Load(object sender, System.EventArgs e)
{
Response.Redirect("~/Home");
}

So the runtime will first set up routes in the global.asax page, then it will process the Default.aspx page. Here it faces a redirect to this URL: /Home.

The Controller

The MVC framework maps this URL to the route set in the global route table, which currently has only the default one, in this format:

Controller/action/id

So /Home corresponds to a controller named Home, and because we have not specified any action or ID, it takes the default values we specified in the RegisterRoutes() method in the globals.asax.cs. So the default action was Index and the default parameter was an empty string. The runtime initializes the HomeController.cs class, and fires the Index action there:

public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Title"] = "Home Page";
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}}

In this Index() method, we set the data to be displayed in the View (aspx/ascx pages) by using a dictionary property of the base Controller class named ViewData. ViewData, as the name suggests, is used to set view-specific data in a dictionary object that can hold multiple name/value pairs. When we call the View() method, the ViewData is passed by the Controller to the View and rendered there.

ASP.NET 3.5 Application Architecture and Design Build robust, scalable ASP.NET applications quickly and easily.
Published: October 2008
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

The View

Let us now look at the View. How does the framework know which View or aspx page to call? Remember that we passed the value "Index" in the action parameter (in the default route in the global.asax.cs file), so the Index.aspx will get called. Here is the code-behind of Index.apsx:

public partial class Index : ViewPage
{
}

There is absolutely no code here, which is a very important characteristic of the MVC design. The GUI should have no logical or data fetching code. Note that the Index class is derived from the ViewPage class. Using this ViewPage class, we can access all of the items in the ViewData dictionary that were set in the controller's Index() method and passed on to the View. Here is how we are accessing the ViewDatain HTML:

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent"
runat="server">
<h2><%= Html.Encode(ViewData["Message"]) %></h2>
<p>
To learn more about ASP.NET MVC visit
<a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
</p>
</asp:Content>

We can directly access the ViewData dictionary in HTML. Now that we have seen how MVC works, we will create a new page to learn how to show data using a custom DAL and strongly typed objects, instead of the ViewData dictionary. Our example page will show a list of all the customers.

The Model

We will use a 5-Tier solution and change the GUI layer to make it follow the MVC design using the ASP.NET MVC framework. Open the 5-Tier solution and delete the ASP.NET web project from it. The solution will then only contain 5Tier.BL, 5Tier.DAL and 5Tier.Common projects.

Right click the solution in VS, and select Add New Project, and then selectASP.NET MVC Web Application from the dialog box. Name this new web project as Test.MVC. This web project will be the new MVC based UI tier of our OMS application.

The Customer.cs and CustomerCollection.cs class files in the business tier (5Tier.Business class library) will be the Model in our MVC application. To show a list of customers, the CustomerCollection class simply calls the FindCustomer() method in CustomerDAL.cs. So we can use an n-tier architecture in an MVC application, hence this shows that MVC and n-tier are not mutually exclusive options while consideringthe application architecture of your web application. Both actually complimenteach other.

We can also create a utility class named CustomerViewData to transfer the Model objects to the View. There are multiple ways to pass-in the Model to the View through the Controller, and creating ViewData classes is one of them. Here is the CustomerViewData class created in the CustomerComtroller.cs file in the Test.MVC web project:

#region ViewData
/// <summary>
/// Class used for transferring data to the View
/// </summary>
public class CustomerViewData
{
public CustomerViewData() { }
public CustomerViewData(Collection<Customer> customers)
{
this.customers = customers;
}
public Collection<Customer> customers;
public Customer customer;
}
#endregion

Notice that this ViewData class is simply wrapping the business object inside it so that we can use this class in the UI layer instead of directly passing and manipulating domain objects.

Wiring Controller, Model, and View

We will now create routes in the global.asax file under the existing home page route as follows:

routes.MapRoute(
"Customer", "Customer/{action}/{id}", new {
controller = "Customer", action = "Show", id="" } );

This new route will simply fire the Show action in the customer controller.
Now we create the controller class, CustomerController, as:

using NTier.BL;
using NTier.Common;
namespace Chapter05.MVC.Controllers
{
public class CustomerController:Controller
{
#region ViewData
/// <summary>
/// Class used for transferring data to the View
/// </summary>
public class CustomerViewData
{
public CustomerViewData() { }
public CustomerViewData(Collection<Customer> customers)
{
this.customers = customers;
}
public Collection<Customer> customers;
public Customer customer;
}
#endregion
public ActionResult Show()
{
CustomerViewData customerViewData = new CustomerViewData();
CustomerCollection customers = new CustomerCollection();
customerViewData.customers
= customers.FindAll(LoadStatus.Loaded);
return View("Show", customerViewData);
}
}//end class
}//end namespace

In this class, we first create a subclass called CustomerViewData, which is a wrapper to hold the Customer and CustomerCollection business objects. We will transfer this class object to the actual view through the controller as:

public ActionResult Show()
{
CustomerViewData customerViewData = new CustomerViewData();
CustomerCollection customers = new CustomerCollection();
customerViewData.customers
= customers.FindAll(LoadStatus.Loaded);
return View("Show", customerViewData);
}

In this controller action, we simply get the list of customers from the database and pass it to the Show.aspx page using the View() method.
To create the aspx pages, we will create a folder named Customers under the View, and add an aspx file named Show.aspx that will display a list of all the customers.

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<asp:Repeater ID="rptCustomer" runat="server">
<ItemTemplate>
<%# Eval("Name")%>
</ItemTemplate>
</asp:Repeater>
</asp:Content>

In the code-behind, we simply bind the data as:

using System.Web.Mvc;
namespace Chapter05.MVC.Views.Customer
{
public partial class Show :
ViewPage <Controllers.CustomerController.CustomerViewData>
{
protected void Page_Load(object sender, EventArgs e)
{
rptCustomer.DataSource = ViewData.Model.customers;
rptCustomer.DataBind();
}
}
}

So the code-behind is kept very light and has no extra code besides data binding to the repeater. Note that usually in the standard postback model, we bind the data under the if(!IsPostBack) condition so that data binding happens only once, on the page load, and not on the postback.
Here we cannot follow the same pattern as there is no concept of a postback in MVC. Each request will be as unique as the RESTful URL(REST refers to Representation State Transfer).
If we want, we can also bind the data in the ASPX using inline code without using code-behind, as shown here:

<h2>Customer list</h2>
<%foreach (var c in ViewData.Model.customers) { %>
<div>
<%=c.Name %>
<br />
</div>
<%} %>

Note the use of the "var" type to get the CustomerData object. This helps us in avoiding explicit casting to get the Customer object.
In the same way, we can edit and add objects. An important point to note is that we don't need to use the standard ASP.NET button controls any more, because we don't want the page to postback to itself. Instead, when we add a customer, we can use something like this:

<form method="post" action="Customer/Add">
<input type="text" name="customerName" value="" />
<input type="submit" name="Add" value="Add" />
</form>

Notice that there is no runat="server" tag here in these controls as they are not server controls but simple HTML controls. On clicking the Add button, the page will post with the action Customer/Add. This means that, in the CustomerController, it will fire the Add method as shown in the sample code (just for demonstration purposes):

public class CustomerController : Controller
{
public ActionResult Add(string customerName)
{
//create a business object and fire the add method
Customer customer = new Customer();
customer.Name = customerName;
CustomerCollection customers = new CustomerCollection();
customers.Add(customer);
return View("Home");
}
}

ASP.NET MVC will automatically set the parameter, customerName, with the value of the textbox from the form's post data, and will pass this value in the Add method of the controller. So we did not have to create the entire page object again, as the page did not postback to the same form. Hence, we avoided recreating the page class on every request or postback.
We will not go into the details of the edit and add actions; it is best to refer to the following post for these details:

http://weblogs.asp.net/scottgu/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx

Unit Testing and ASP.NET MVC

Unit testing is the process where the developer himself tests the code that he has written. The word "unit" here refers to modules of code that he writes, such as methods, functions, and so on. The developer would test such "units" code to verify whether everything is working as expected. This will make sure that at least the individual methods of a class work without errors. Unit testing is different from the function or integration testing that a QA (quality assurance) person performs on a working model after the development phase is over. Unit testing is more closely related to the actual code testing and ensures that most of the bugs are taken care of before the project reaches the actual testing stage. Because unit testing checks the actual methods in the code, it can easily be automated by creating mock test casesin the code using one of the many available unit testing frameworks, such as NUnit and MBUnit.
Unit testing the GUI of our ASP.NET projects is highly important; but it is difficult to do so under the standard page controller model. However, in ASP.NET MVC, we have "thin" code-behind classes, with almost little or no code. There are almost no Session and ViewState related issues, as all URLs are handled directly by the central controller. There are no button clicks, no code-behind event handlers, and so on. So it is easy to set up unit test cases for the controller classes because then it is the same as testing any normal C# class methods, such as the DAL, the BL, and so on.

Summary

ASP.NET MVC is a very good platform for creating unit-testable applications that are maintainable in the long run, as well as for achieving a clearer separation between the UI and the UI-handling logic.The core principle of the ASP.NET MVC framework is that the URL should talk directly to the requested resource in the web application. It is a very good choice for creating a unit-testable and search engine friendly web application which makes our web UI much cleaner by having a clear separation between the UI and code logic.


Further resources on this subject:


ASP.NET 3.5 Application Architecture and Design Build robust, scalable ASP.NET applications quickly and easily.
Published: October 2008
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Vivek Thakur

Vivek is passionate about architecting and developing applications based on Microsoft .NET platform using ASP.NET, C#, VB.NET, and MS AJAX. He has authored several technical articles on ASP.NET and has also been an All-Star-level contributor on ASP.NET forums. Vivek’s passion for ASP.NET has been formally recognized by the Most Valuable Professional (MVP) award given to him by Microsoft in April 2007, and again in 2008. He is also a Subject Matter Expert for Microsoft ASP.NET 3.5 Certification Exams. He is a leading contributor and moderator in the CodeAsp.Net forums. Vivek is currently working as the Managing Partner in Axero Solutions LLC, a US-based software product development and business consulting firm.

Although his expertise lies in Microsoft's .NET platform, Vivek is also knowledgeable in J2EE and C/C++. He has a deep interest in programming, chaos theory and artificial intelligence, and is a strong advocate of chaos theory in software systems and management.

Besides his love for software architecture and design, Vivek also focuses on project management skills and has substantial experience in managing small to medium sized projects. He has also conducted numerous training sessions and provided concept-based tutoring in different software firms across India.

Vivek received his Bachelors degree in engineering from the Indian Institute of Technology (IIT), New Delhi, India.

Contact Vivek Thakur

Books From Packt

ASP.NET Data Presentation Controls Essentials
ASP.NET Data Presentation Controls Essentials

Entity Framework Tutorial
Entity Framework Tutoria

Microsoft Visual C++ Windows Applications by Example
Microsoft Visual C++ Windows Applications by Example

Software Testing with Visual Studio Team System 2008
Software Testing with Visual Studio Team System 2008

Microsoft AJAX Library Essentials: Client-side ASP.NET AJAX 1.0 Explained
Microsoft AJAX Library Essentials: Client-side ASP.NET AJAX 1.0 Explained

DotNetNuke Skinning Tutorial
DotNetNuke Skinning Tutorial

LINQ Quickly
LINQ Quickly

WCF Multi-tier Services Development with LINQ
WCF Multi-tier Services Development with LINQ

 


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software