Click here to Skip to main content
6,291,522 members and growing! (12,897 online)
Email Password   helpLost your password?
Web Development » ASP.NET » General     Intermediate License: The Common Development and Distribution License (CDDL)

ASP.NET MVC - Part 1

By Mark Nischalke

A look at the ASP.NET MVC Application in ASP.NET Extensions 3.5
C# (C# 3.0), HTML, .NET (.NET 3.0, .NET 3.5), ASP.NET, LINQ, CEO, Architect, Dev, Design
Posted:23 Mar 2008
Updated:7 Apr 2008
Views:36,132
Bookmarked:68 times
Unedited contribution
Announcements
Loading...
 
Search    
Advanced Search
printPrint   Broken Article?Report       add Share
  Discuss Discuss   Recommend Article Email
23 votes for this article.
Popularity: 4.56 Rating: 3.35 out of 5
8 votes, 40.0%
1
1 vote, 5.0%
2
1 vote, 5.0%
3
1 vote, 5.0%
4
9 votes, 45.0%
5

Introduction

There are many ways to create ASP.NET web applications; however, one common problem with them all is the difficulty in separating business logic from presentation. Separating these layers allows for more modular development, code reuse, and ease of testing. A pattern that supports these features is the Model-View-Controller, or MVC. The ASP.NET team has recognized the benefits of this pattern and is working to incorporate it into ASP.NET. This article will focus on building an ASP.NET MVC web application from the Preview 2 release of ASP.NET 3.5 Extensions.

Prerequisites

ASP.NET 3.5 Extensions, Preview 2 http://www.asp.net/downloads/3.5-extensions/

MusicCatalog database, included in download.

Model-View-Controller Pattern

The first thing that must be discussed, of course, is what the MVC pattern is. MVC is a pattern that divides an application into separate areas of responsibility; Model, View and Controller.

Model: Models are responsible for maintaining the state of the application, often by using a database.

View: These components are strictly for displaying data, they provide no functionality beyond formatting it for display.

Controller: Controllers are the central communication mechanism in a MVC application. They communicate actions from the views to the model and back.

One of the main points with the MVC pattern is that there is no direct communication between the model and view. This allows for reuse of model and controller code in different types of applications. The logic for a WebApplication could easily be applied to a Windows application by changing the view components. The controller components could also be exposed as WebServices for SOA without affecting the view. Of course the model could also be changed without affecting the controller; for instance, if the database itself or the scheme were changed. Another benefit to separating the functions is to allow for better testing. Since the view is only concerned with display, all of the logic that needs tested is in the controller. Unit tests can easily be incorporated to test these functions.

ASP.NET Extentions

ASP.NET 3.5 Extensions Preview is a set of enhancements to ASP.NET and ADO.NET that are expected to be included in future releases but are available now as previews. These enhancements include ASP.NET Silverlight controls, ADO.NET Entity Framework, ASP.NET Dynamic Data and ASP.NET MVC. The last is of course what we will be focusing on here. For more information about the others please see the references at the end of this article.

All of the functionality for ASP.NET MVC projects is provided in three assemblies.

• System.Web.Mvc
• System.Web.Routing
• System.Web.Abstractions

Creating an ASP.NET MVC project

After installing the extensions you should see a new project type under File -> New Project -> Visual C# -> Web. This project is only available in C#. There are no plans at this time to support VB.

mvc1.png

The first thing this project wizard asks is if you want to create a unit test project. As stated earlier one of the benefits of the MVC pattern is the separation of logic for easy testing. Although unit testing will be covered in a future article, we’ll create the test project along with the main Web Application project.

mvc2.png

As we can see below the project creation wizard will create the basic shell of the Web Application, including a Master page and two views, About and Index, and the HomeController. The test project has also been created and added to the solution, with unit test for the HomeController.

mvc3.png

Music Catalog Application

We will use this basic shell to build a simple demo app that displays artists, albums associated with the artist, and the songs associated with the album. Later in this series we will also create forms for editing and inserting data. The database script provided with this article includes all the data we will be working with.

Applying the MVC Pattern

The Model

Since the application isn’t very useful without data, we will start by creating a model for the MusicCatalog. To simplify development we will be using LINQ to SQL.

Add New Item -> Data -> LINQ to SQL Classes. We’ll name the class Music.

mvc4.png

After the clicking the Add button, a designer will be displayed with two panels. The right panel allows you to drag stored procedures from a database onto this surface to create methods in the generated class. We’ll skip this for the time being and concentrate on the left panel. This panel allows you to drag database tables from the server explorer onto the surface and create classes, with access methods from them. For this demo, open the server explorer and navigate to the MusicCatalog database. Drag the three tables, Artist, Album and Song onto the designer surface.

A brief look at LINQToSQL Classes

As you can see after the tables have been drug to the design surface, the foreign key relationships established in the database have also been represented in the designer and generated classes. If we open the Music.designer.cs file, we can see the classes that have been created.

[System.Data.Linq.Mapping.DatabaseAttribute(Name="MusicCatalog")]
public partial class MusicDataContext : System.Data.Linq.DataContext 

Notice here that DataContext has automatically been appended to the name we provided, Music. It is the convention to name LINQToSQL classes this way. We can see also that LINQToSQL uses the DatabaseAttribute to identify the database this class represents.

Partial Methods

The generated class also contains definitions for methods that have been declared as partial.

#region Extensibility Method Definitions
partial void OnCreated(); 
partial void InsertArtist(Artist instance); 
partial void UpdateArtist(Artist instance); 
partial void DeleteArtist(Artist instance); 
partial void InsertAlbum(Album instance); 
partial void UpdateAlbum(Album instance); 
partial void DeleteAlbum(Album instance); 
partial void InsertSong(Song instance); 
partial void UpdateSong(Song instance); 
partial void DeleteSong(Song instance); 
#endregion

This is a new feature added in .NET 3.0 that is similar to the partial class that was introduced in .NET 2.0. Partial classes allow code to be separated into multiple classes and complied into a single unit. An example is the separation of ASP.NET code behind files. Partial methods allow a designer to implement define and stub in methods that may be implemented by developers using the class. One difference between partial methods and virtual methods is that if the method is not implemented, the compiler just ignores it, as can be seen below.

mvc5.png

However, when the method is implemented in a partial class, it is used and compiled into the assembly.

mvc6.png

A usage for this technique is for simple lightweight messaging capability. It is also useful for providing hooks into your code that other developers can use to provide additional functionality, such as auditing, without the need to drive additional classes.

Adding accessor methods

The DataContext class isn’t very useful as is, since it doesn’t really provide methods to access the data we need, such as getting a list of the albums associated with a given artist. So we’ll add some methods now. These could be provided by stored procedures in the database and dragged to the design surface to be automatically generated. However, by implementing them ourselves we can look at some LINQ methods and techniques.

public Artist GetArtistById(int id)
{ 
   return Artists.Single(a => a.id == id); 
}
public List<Album> GetAlbumsForArtist(int id) 
{ 
   return Albums.Where(a => a.artist_id == id).ToList(); 
}

These two methods are only a sample; see the MusicDataContext class for full implementation. They show using the Single and Where LINQ extension methods to return an Artist, matching the given id, and a List of Albums for the for the given Artist id. Since this article isn’t about LINQ link we’ll move on and leave the details to other articles.

The Controller

All of the controllers in this application are located under the appropriately named Controllers folder. In an ASP.NET MVC application, users don’t make requests for pages or resources, instead they request actions. Each public method in a controller is an action that can be requested.

The default implementation provided by the project template includes one controller, HomeController, with two actions, Index and About. When creating a new controller class, it must have the suffix Controller. MVC uses reflection to locate controllers based on the names, as we will see shortly, with this suffix. The reason for this requirement unknown.

public class HomeController : Controller
{ 
   public void Index() 
   { 
      RenderView("Index"); 
   } 
   public void About() 
   { 
      RenderView("About"); 
   } 
} 

We’ll cover the RenderView method shortly, so for now just ignore them.

MusicController

To create the controller for the MusicCatalog application, right click on the Controllers folder in the MVC project and select Add -> New Item or Class and select MVC Controller Class in the Add New Item dialog. Notice the description stating that the class must use the Controller suffix as mentioned earlier.

mvc7.png

The class that is created is derived from System.Web.Mvc.Controller and already contains one method, Index. This is the default action for any controller.

public class MusicController : Controller
{ 
   public void Index() 
   { 
      // Add action logic here 
   } 
} 

We will add an instance of the previously created Model, MusicDataContext, and methods for the actions necessary to retrieve and display Artists, Albums and Songs.

public class MusicController : Controller
{ 
   private MusicDataContext m_Music = new MusicDataContext(); 
   public void Index() 
   { 
      // Add action logic here 
   } 
   public void Artists(string letter) 
   { 
      RenderView("Artists", DataContext.GetArtists(letter)); 
   } 
   public void Albums(int id) 
   { 
      RenderView("Albums", DataContext.GetAlbumsForArtist(id)); 
   } 
   public void Songs(int id) 
   { 
      RenderView("Songs", DataContext.GetSongsForAlbum(id)); 
   } 


  
   #region Properties 
   private MusicDataContext DataContext 
   { 
      get { return m_Music; } 
   } 
   #endregion 
} 

Rendering the view

The controller initiates the process of having a page displayed to the user by calling RenderView, which has a few overloads. In the overload version we are using, the first parameter is the name of the view page to be rendered. Notice we do not need to specify a full path, only the name. The MVC framework will look for this page in a folder matching the name of the controller under the views folder. The second parameter to the RenderView method is an object that will be past to the view. This object is assigned to the ViewData member, which is an implementation if IDictionary<string, object>. You can assign ViewData directly and use an alternate override of RenderView, as below.

public void Artists(string letter)
{ 
   ViewData["Artists"] = DataContext.GetArtists(letter); 
   RenderView("Artists");
}

Of course, since ViewData is an IDictionary<string, object>, we can assign multiple values to be passed to the View.

public void Index()
{ 
   ViewData["TheAnswer"] = 42; 
   ViewData["Data"] = DataContext.GetArtists("A"); 
   RenderView("Artists"); 
   //RenderView("Artists", DataContext.GetArtists("A")); 
}

If the second RenderView method, commented out above, is used it will overwrite any previous assignments of ViewData with the List<Artists> returned from DataContext.GetArtists(“A")

The View

Now that we have the Model and Controller it’s time to move on to the Views. We’ll start by adding the Artist view to display all of the music Artists in the database.

First add a new folder under Views called Music. The name of this folder matches the name of the controller it relates to. Notice the Home folder with views matching the actions in the HomeController that was created by the project template. Next, right click the folder you just created and select Add -> New Item

mvc8.png

As you can see, there are four items that relate to MVC projects, Master Page and User Control should be obvious. The difference between MVC View Page and MVC View Content Page is the latter is for use with a MasterPage, while the former is a stand-alone page. We will select MVC View Content Page and name it Artists.aspx. Select Site.Master under the Shared folder when asked for a MasterPage to use.

One thing to keep in mind with MVC View pages is that they are not ASP.NET pages. Although they have the aspx extension for convenience, they have no form, as the below MVC View Page sample shows. All interactions are processed via controllers, not by forms, as in a traditional ASP.NET application. Though they are not ASP.NET forms, server controls can still be used, as we will see shortly.

<html xmlns="http://www.w3.org/1999/xhtml" > 
<head runat="server"> 
   <title></title> 
</head> 
<body> 
   <div> 
   </div> 
</body> 
</html>

ViewPage Code behind

Opening up Music.aspx.cs you can see the class is a partial class, just like an ASP.NET page, but derives from ViewPage rather than Page.

public partial class Artists : ViewPage

Within this class you can use the same methods and events you would in a normal ASP.NET page, such as the Load or DataBind events. In our case, we’ll override the Load event

protected override void OnLoad(EventArgs e) 
{ 
   AddMenu(); 
   ArtistList.DataSource = (List<Artist>)ViewData["Artists"];    
   ArtistList.DataBind();
}

We start by adding a simple alphabetic menu to the top of the page for navigation; more on that in a moment. Next, we bind the ViewData, which was passed from the controller, to the ListView control. Remember that the ViewData was also a member of the controller class. Keeping the same name between the classes is a convenience.. Since ViewData is an IDictionary<string, object>, we need to cast it from an object to a generic List<Artists> before using it. This loose coupling is good, but what if we needed more strongly typed data. ViewPage also has a generic constructor that allows you to specify the type ViewData will be, making any casting, and the inherent performance penalties from possible boxing/unboxing operations, unnecessary. We also gain Intellisence support and compile time error checking.

public partial class Artists : ViewPage< List<Artist> >

Now ViewData can be used as List rather than as a generic object, as in the first image.

mvc9.png

mvc10.png

Creating a menu

To be somewhat useful this application needs a way to list the Artists in the database so users can select them. A simple alphabetic menu should serve our needs.

mvc11.png

We construct the menu by simply iterating from A to Z, creating an html link for each letter and adding it to a container. Nothing special here, except for creating the link.

private void AddMenu()
{ 
   // Build alphabetic menu 
   for(char c = 'A'; c <= 'Z'; c++) 
   { 
      string link = Html.ActionLink(c.ToString(), "Artists", 
         new RouteValueDictionary( new { controller = "Music", 
            letter = c.ToString() }) ); 

      
      Alphabet.Controls.Add(new LiteralControl(link)); 
      // Add seperator 
      if(c != 'Z') 
         Alphabet.Controls.Add(new LiteralControl(" | ")); 
   } 
}

ViewPage has a HTML property that exposes an HtmlHelper class. This class, as the name implies, provides helper methods for creating links html links, ActionLink and RouteLink. Remember that in a MVC application everything is routed through controllers that take action as necessary, hence the method ActionLink. We don’t create Html links that points to a URI, but rather links that tell what action to request from a controller.

In the above example, the first parameter is the text that will appear on the link. The next parameter is the name of the action to request. The third parameter, as we can see, is a RouteValueDictionary object that uses object initialization to assign a value to the controller and letter properties. This generates an html anchor tag that matches the route format, {controller}/{action}/{letter}, and looks like this: <a href="/Music/Artists/A">A</a>

URL Routing

Now that we have the basic architecture of the application laid out, and, hopefully, an understanding of the major pieces, the question is; How does the application know how and when to display a particular page? The answer is URL Routing.

A route is the path, or URL in traditional web applications, to an action in a controller. An ASP.NET MVC application adds a RouteTable object to the application scope for this purpose. RouteTable, defined in the System.Web.Routing namespace, contains a single property, Routes, which is a RouteCollection.

The ASP.NET MVC project template adds this implementation to the Gloabal.asax.cs file.

public class GlobalApplication : System.Web.HttpApplication
{ 
   public static void RegisterRoutes(RouteCollection routes) 
   { 
      routes.Add(new Route("{controller}/{action}/{id}", new MvcRouteHandler())
      { 
         Defaults = new RouteValueDictionary(new { action = "Index", id = "" }), 
      }); 

      routes.Add(new Route("Default.aspx", new MvcRouteHandler()) 
      { 
         Defaults = new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }), 
      }); 
   } 
   protected void Application_Start(object sender, EventArgs e) 
   { 
      RegisterRoutes(RouteTable.Routes); 
   } 
} 

URL Routing uses pattern matching to direct the request to the appropriate controller and action and routes are evaluated in the order in which they have been registered. Just like exception handling, you should register routes from most specific to general.

The first route added to the collection is for requests using the format, {controller}/{action}/{id}, such as, Home/About/1. The second route is a default catch all. Typically in a web application a request with no resource, only the application name, such as, http://www.mymvcapp, will be routed to a default page, usually default.aspx for ASP.NET applications.

In both routes you can see that a Defaults property is also being assigned. This property is of type RouteValueDictionary, which is an IDictionary<string, object>, and as you can guess by the name, stores a collection of defaults to be used for the route. In the second route since no value has been provided for a controller or action, the values specified for each in the Defaults property are used, Home and Index respectively in this case. The first route registered above will only match if a controller has been specified in the request, but if an action or id has not been included, the defaults will be used for those values.

To be continued…

ASP.NET MVC is a very rich and detailed technology that can’t be covered in a single article. Hopefully this article has illustrated the basic concepts and can be used to evaluate the great potential of this technology. Future articles in this series will cover a more in depth look at URL routing, posting data to a database or processing input from a page and unit testing.

References

http://www.asp.net/downloads/3.5-extensions/
http://weblogs.asp.net/scottgu/archive/2007/10/14/asp-net-mvc-framework.aspx

Caution: This series of articles is based on an earlier release, many things have changed with the preview 2 release.

Partial methods

http://blogs.msdn.com/wesdyer/archive/2007/05/23/in-case-you-haven-t-heard.aspx
http://community.bartdesmet.net/blogs/bart/archive/2007/07/28/c-3-0-partial-methods-what-why-and-how.aspx

History

Part 1 posted 3/23/08

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)

About the Author

Mark Nischalke


Member
My first job was coding COBOL on Mainframes but I soon progressed to DOS, then to Windows. I've been a consultant for a number of years and have been involved in a variety of projects, from Finite Element Analysis systems to train control systems to large e-commerce websites to one page static websites. I attained my MCSD status in 1998 with the WOSA exams, updated it with VC++ and transitioned to MCSD.NET, grabbing MCAD.NET along the way. I've been working almost exclusively with C# and .NET since it was in beta.
Occupation: Other
Company: MANSoftDev
Location: United States United States

Other popular ASP.NET articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 34 (Total in Forum: 34) (Refresh)FirstPrevNext
GeneralMy vote of 1 Pinmember32hrBookThief9:17 8 Dec '08  
GeneralRe: My vote of 1 PinsupporterMark Nischalke9:30 13 Apr '09  
GeneralMy vote of 1 PinmemberMember 449903717:48 29 Nov '08  
GeneralRe: My vote of 1 PinsupporterMark Nischalke9:29 13 Apr '09  
GeneralMVP PinmemberNick Naddaf3:46 14 Nov '08  
GeneralRe: MVP PinsupporterMark Nischalke9:33 13 Apr '09  
GeneralUsing ASP.NET MVC Preview 3 for .Net 2.0 Framework Pinmemberclarenz17:14 25 Aug '08  
GeneralRe: Using ASP.NET MVC Preview 3 for .Net 2.0 Framework PinsupporterMark Nischalke17:21 25 Aug '08  
GeneralRe: Using ASP.NET MVC Preview 3 for .Net 2.0 Framework Pinmemberclarenz21:40 25 Aug '08  
GeneralRe: Using ASP.NET MVC Preview 3 for .Net 2.0 Framework PinsupporterMark Nischalke3:33 26 Aug '08  
GeneralGreat for starting with Pinmembermelchizidech7:42 19 Aug '08  
GeneralGetting string entered by the user in the controller PinmemberMarwanZakhia22:07 5 Aug '08  
GeneralDependency Injection (DI) PinmemberMarwanZakhia2:49 8 Jul '08  
GeneralRe: Dependency Injection (DI) PinsupporterMark Nischalke3:02 8 Jul '08  
GeneralProblem with html.actionlink in asp.net MVC framework Pinmembermynameissuraj22:24 6 Jul '08  
GeneralRe: Problem with html.actionlink in asp.net MVC framework PinsupporterMark Nischalke2:43 7 Jul '08  
GeneralRenderView PinmemberMarwanZakhia20:00 3 Jul '08  
GeneralRe: RenderView PinsupporterMark Nischalke5:50 4 Jul '08  
GeneralStarting MVC Pinmembermynameissuraj0:10 3 Jul '08  
GeneralRe: Starting MVC PinsupporterMark Nischalke3:19 3 Jul '08  
GeneralThe name 'RenderView' does not exist in the current context Pinmemberrabi825:48 1 Jun '08  
GeneralRe: The name 'RenderView' does not exist in the current context PinsupporterMark Nischalke10:16 1 Jun '08  
GeneralPart 2 Pinmembernmq7:08 20 Apr '08  
GeneralRe: Part 2 PinsupporterMark Nischalke8:34 20 Apr '08  
Generalsource code not available ? PinmemberQuachNguyen7:38 29 Mar '08  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 7 Apr 2008
Editor:
Copyright 2008 by Mark Nischalke
Everything else Copyright © CodeProject, 1999-2009
Web16 | Advertise on the Code Project