Saturday, June 4, 2011

ASP.NET MVC Framework

This is the first post of ASP.NET MVC series, as promised a while earlier. There is enough information on the Internet already by now, that ASP.NET MVC 3 is already released. The posts will contain links to the required sites and of course a step by step guide to develop applications using MVC.

ASP.NET MVC Framework site is the central place where (mostly) all the information regarding installing and using MVC framework is available. The current release MVC3 can also be downloaded from here.

What is MVC? MVC stands for Model-View-Controller. In my earlier posts on framework, I detailed the 3-tiered architecture of software development and explained the service-oriented concepts too. MVC is a different story altogether. It is a new way to developing applications, but a better way than the rest. I personally, like MVC the most (since I read about it in the the Design Patterns book by Erich Gamma and team although it was a short introduction in SmallTalk there). In MVC pattern, there are obvious three components (call them layers):
  1. Models are components of application that maintain state. Think of them as a combination of data access layer that provides data from the server to the interface and stores them back and business logic layer consisting of some business logic to manipulate data.
  2. Views are components of application that display the user interface. Think of them as the presentation layer consisting HTML output renders.
  3. Controllers are components of application that handle events and act as the communication bridge between Models and Views. They are responsible of handling user interaction and resulting in rendered views.
Although, I tried to relate these layers to the 3-tiered architecture, to help those familiar with 3-tiered pattern to correlate, there is a huge difference in practice. To understand MVC, however, one must keep aside the other classic patterns that I discussed earlier aside. MVC is a new adventure.

To start with, you need to have the usual Visual Studio development environment. Get Visual Studio Express and SQL Server 2008 Express for the experiments, if you do not have them already. MVC is not going to change anything existing. The code will be written in C# or VB.NET, the ASPX, ASCX and master pages will remain; so nothing scary. MVC is an addition to the already charming bouquet of development tools and frameworks Microsoft provides. And by far it is the best (personal opinion).

Install MVC from the ASP.NET MVC site. To install MVC 2, .NET Framework 3.5 is required and for MVC 3, .NET Framework 4.0 is required.


Once the MVC framework is installed, a new project templates are installed, that can be used to develop MVC applications. For this post, I will choose MVC 2 template (and not choosing the option to create unit test project for now). In next posts, I will discuss MVC 3 and Razor view as well. On choosing the MVC 2 template, Visual Studio creates a solution with the project. What we notice is that a few files are already existing in the project.

Note the folders Controllers, Models and Views. As described above, these three folders contain their respective classes, i.e. all controller classes are placed in Controllers folder and so on. When the project is run, it opens the home page. But this should come as a surprise and a question to many who are new to MVC. First, there is no default.aspx page. Second, the Index.aspx page is inside Views folder, not in the root directory. How is the page even fetched? And why does not the URL show the page name that is being called!

Welcome to the first MVC culture shock! I said earlier, MVC is a completely new adventure. The most important thing to notice in MVC is that the URL does not map to a physical file (e.g. an aspx page). Instead, the URL maps to a controller class. So instead of a URL like http://localhost/Library/Book.aspx which would open the Book.aspx file, the URL in MVC looks like http://localhost/Library/Book and it maps to BookController class. We will talk about URL mapping again later.

Now, about the question on how does the server know which page to open as the default page. The secret lies in Global.asax file.
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 = UrlParameter.Optional } // Parameter defaults
            );

        }
The Global.asax.cs file has a method named RegisterRoutes. This is the location where the URL mapping is listed out. We will talk about routes again when we talk about URL routing, but for now, refer the section where a MapRoute method is called mentioning the Default route should be the "Home" controller and the action (the view action) should be Index. This is why the first time the site runs, the HomeController class is called and its method Index is called (since action was specified as Index). The Index action actually returns a type ActionResult which is nothing but a view named Index which should exists in Views/Home/Index.aspx. This should give a glimpse on how the folders are structured.

To summarize, the Controllers are the entry point of an application. The URL maps to a Controller class. A controller class contains action methods. For each controller, a folder is created in Views folder and in this folder, the views for each action specified is placed. So, if HomeController class has two action Index and About, the folder Views/Home should contain Index.aspx and About.aspx. If the URL provides an action to be called, then the specific action of the controller is called, otherwise the Index action is called by default. So a typical URL would be of the following format:

/Controller/Action/Parameters

Lets try this by creating a new controller. To add a new controller, right click on Controllers folder in solution explorer and choose Add > Controller. A new controller class is added.
namespace Library.Controllers
{
    public class BookController : Controller
    {
        //
        // GET: /Book/

        public ActionResult Index()
        {
            return View();
        }
    }
}
Notice a few things. The controllers belong to the ProjectName.Controllers namespace by default. Each controller class inherits from the Controller base class. And an action Index is added by default.

The controller gets data from Model and feeds it to the View. So, lets add a model class. Right click on Models folder in solution explorer and choose "Add > Class". Notice the namespace Projectname.Models.
namespace Library.Models
{
    public class Book
    {
        public int bookID;
        public string bookName;
        public string authorName;

        public Book(int ID, string name, string author)
        {
            bookID = ID;
            bookName = name;
            authorName = author;
        }
    }
        
    public class BookCollection
    {
        List<Book> booksCollection = new List<Book> { };

        public void AddBook(int bookID, string bookName, string author)
        {
            booksCollection.Add(new Book(bookID, bookName, author));
        }

        public List<Book> List()
        {
            return booksCollection;
        }
    }
}
Let me explain what we have here. In the namespace Library.Models (where Library is my project name), I have created two classes: Book and BooksCollection. The Book class is a simple entity structure holder with a constructor. The BookCollection class has methods to add books to a local list and to return the list. To start with, we have no books! ;).

For this example (since we do not have a database), I will fill the data through controller. Practically, data should flow from database into model and then controller should pass the model to view. So now our Index method in BookController class should be like:
public ActionResult Index()
{
    Models.BookCollection books = new Models.BookCollection();

    books.AddBook(1, "Five point someone", "Chetan Bhagat");
    books.AddBook(2, "2 states", "Chetan Bhagat");
            
    return View(books);
}
Simple enough. The model is populated with two book details and passed to the view. Now, lets add a view for the Index action. The easiest way to do this is to right click on the Index method in the code window of HomeController class and choose "Add view", choosing the default master page for now. A folder for the new controller is added in Views folder and an Index.aspx file is created. Lets use the message to display it in the content section.
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Library.Models.BookCollection>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 Index
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Book List</h2>

    <div>
        <% foreach (Library.Models.Book book in Model.List())
            { %>
            <li><%: book.bookID.ToString() + ": \"" + book.bookName + "\" by " + book.authorName + "." %></li>
        <% } %>
    </div>

</asp:Content>
Notice in the page directive that I used Library.Models.BookCollection to be the model that the view will be dealing with. (so now a reference to Model in the view page refers to BookCollection model instance passed from controller).

Run the project and use the URL http://localhost:[port]/Library/Book to invoke the BookController's default Index action. There you go! A simple example to use Model-View-Controller.

Lets look at few noticeworthy points before I close this post. The controller class imports from "System.Web.Mvc" which is important because this is the namespace that contains classes to support MVC framework. e.g. ViewData is a dictionary object that is defined in this namespace.Also, the use of "<%:" is new in the view page. This syntax was introduced in .NET framework 4 and it encodes the HTML output before it is sent to output buffer, in contrast to "<%=" that did not encode the HTML before buffering the output.

Keeping it simple for the first post, I will close it here today. ASP.NET MVC has matured and is in its 3rd version now. And there is lots can be done with MVC that I will post subsequently.