Tuesday, October 7, 2008

ASP.NET 3.5 MVC

This model view controller (MVC) framework for ASP.NET provides a structured model that enables a clear separation of concerns within web applications, and makes it easier to unit test your code and support a TDD workflow. It also helps provide more control over the URLs you publish in your applications, and more control over the HTML that is emitted from them.
ASP.NET 3.5 extensions, which contain the MVC Framework (Model View Controller), ASP.NET AJAX Improvements, ASP.NET Silverlight Support, ADO.NET Data Services and ASP.NET Dynamic Data Support.
The MVC architecture has long been used in Web app frameworks like Ruby on Rails and Apache Struts. Now, Microsoft is pushing ASP.NET Web application development in the direction of MVC, with the release of the ASP.NET 3.5 Extensions.
MVC and the web seem like a match made in heaven and allow for a simple way of building complex web applications. To understand why this recently re-discovered way of building web application was developed, we need to have a look at the pitfalls of the standard model, which is the web-form approach.
To understand why this recently re-discovered way of building web application was developed, we need to have a look at the pitfalls of the standard model, which is the web-form approach.
First, it is event based: it can be good or bad depending on how you look at it. Good because it helps VB6 and WinForms developers to smoothly migrate their skills to the web application development. Bad because there are dozens of events that are raised during the page life-cycle, and it's not trivial to understand where to put your code. Also because the process logic is tightly coupled with the page life-cycle, it is difficult to test using automated tests.
It also uses server forms and ViewState: again, as with the event based model, it can be good since this hides to the developer all the problems related to maintaining the state of the page (values of textboxes, contents of dropdowns and so on), but can also be bad if you want to control exactly how the page is rendered, and you don't need to maintain all the state.
Furthermore, it uses server controls: good because they render the HTML for you; bad since they render it the way they want.
With the MVC framework you gain back the control of the order in which things happen during the page life-cycle, of the way state is persisted between requests, and the code with which HTML is rendered. And thanks to a better separation of concerns it's easier to test the process logic. But all this control has a cost: you have to do everything by yourself.
So keep in mind this: the MVC framework is not a replacement for the web-form programming model, but it's an alternative programming model, for those who want to have a better control and want to be able to also test the presentation logic.
To build an ASP.NET MVC application you need to install:
Visual Studio 2008

ASP.NET 3.5 Extensions
Open up your VS2008, File > New > Project. There are two project templates available after you install the extensions:
ASP.NET MVC Web Application: creates a sample web application
ASP.NET MVC Web Application and Test: creates a sample web application and testing project to the test the sample controllers
So select "ASP.NET MVC Web Application":

This creates a project that is pre-populated with all the basic components of the MVC application:
Folders in the application : Following folders are created in MVC applcation:
Content: this folder is meant to include all the styles and images for the UI of the web application.
Controllers: this folder will contain all the classes for the controllers.
Models: the classes for the model will be located in this folder. In a simple ASP.NET application they could be a DataSet, a LINQ model or, in a more complex application, this could contain a wrapper to the Business Logic which could live in a separate assembly.
Views: separated in one folder per controller, here are all the Views (the aspx pages) that will be rendered back to the user.
Views\Shared: this last "standard" folder contains anything that pertains to the Views, but not specific to just one View (for example, Master Pages, shared controls and so on).
As we saw, all the views, the aspx pages, are located in a sub-folder, with the following relative path: /Views/Home/Index.aspx. Does this mean that a user that wants to go the home of my application must type in the browser's address bar the URL: http://www.example.com/Views/Home/Index.aspx?
The answer is no: as I said a few paragraphs above, during a typical request life-cycle, the Controller is the first element being hit, and the decision of which View to render is one of the last steps of the process. This is possible because ASP.NET MVC uses a URL Routing module which allows the URL used to access a given functionality to be disconnected from the physical location of the file that contains it, be it an ASPX View or the Controller class.
This way, instead of having http://www.example.com/ProjectReport.aspx?projId=56, the URL will be http://www.example.com/Projects/Report/56. This is better, both from a SEO (Search Engine Optimization) and usability standpoint.

URL ROUTING :
This an important feature of every ASP.NET MVC application called URL Routing. The URL Routing module is responsible for mapping incoming browser requests to particular MVC controller actions.
First, URL Routing is enabled in your application’s Web configuration file (Web.config file). There are four sections in the configuration file that are relevant to routing: the system.web.httpModules section, the system.web.httpHandlers section, the system.webserver.modules section, and the system.webserver.handlers section. Be careful not to delete these sections because without these sections routing will no longer work.
Second, and more importantly, a route table is created in the application’s Global.asax file. The Global.asax file is a special file that contains event handlers for ASP.NET application lifecycle events. The route table is created during the Application Start event.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace MyApp
{
   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);
  }
 }
}

When an MVC application first starts, the Application_Start() method is called. This method, in turn, calls the RegisterRoutes() method. The RegisterRoutes() method creates the route table.
The default route table contains a single route (named Default). The Default route maps the first segment of a URL to a controller name, the second segment of a URL to a controller action, and the third segment to a parameter named id.

Imagine that you enter the following URL into your web browser’s address bar:
/Home/Index/3
The Default route maps this URL to the following parameters:
controller = Home
action = Index
id = 3
When you request the URL /Home/Index/3, the following code is executed:
HomeController.Index(3)
The Default route includes defaults for all three parameters. If you don’t supply a controller, then the controller parameter defaults to the value Home. If you don’t supply an action, the action parameter defaults to the value Index. Finally, if you don’t supply an id, the id parameter defaults to an empty string.


Let’s look at a few examples of how the Default route maps URLs to controller actions. Imagine that you enter the following URL into your browser address bar:
/Home
Because of the Default route parameter defaults, entering this URL will cause the Index() method of the HomeController class in Listing 2 to be called.


(HomeController.cs is coded as following) - Listing 2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MyApp.Controllers
{
[HandleError]
public class HomeController : Controller
{
public ActionResult Index(string Id)
{
return View();
}
}
}

In Listing 2, the HomeController class includes a method named Index() that accepts a single parameter named Id. The URL /Home causes the Index() method to be called with an empty string as the value of the Id parameter.
Because of the way that the MVC framework invokes controller actions, the URL /Home also matches the Index() method of the HomeController class in Listing 3.


(Listing 3 – HomeController.cs (Index action with no parameter) )
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MyApp.Controllers
{
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
}

The Index() method in Listing 3 does not accept any parameters. The URL /Home will cause this Index() method to be called. The URL /Home/Index/3 also invokes this method (the Id is ignored).
The URL /Home also matches the Index() method of the HomeController class in Listing 4.
Listing 4 – HomeController.cs (Index action with nullable parameter)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MyApp.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
      public ActionResult Index(int? id)
      {
          return View();
      }
    }
}


In Listing 4, the Index() method has one Integer parameter. Because the parameter is a nullable parameter (can have the value Nothing), the Index() can be called without raising an error.


Creating a Custom Route
For many simple ASP.NET MVC applications, the default route table will work just fine. However, you might discover that you have specialized routing needs. In that case, you can create a custom route. Imagine, for example, that you are building a blog application. You might want to handle incoming requests that look like this:
/Archive/12-25-2009
When a user enters this request, you want to return the blog entry that corresponds to the date 12/25/2009. In order to handle this type of request, you need to create a custom route.
The Global.asax file in Listing 6 contains a new custom route, named Blog, which handles requests that look like /Archive/entry date.

Listing 5 – Global.asax (with custom route)
using System.Web.Mvc;
using System.Web.Routing;
namespace MyApp
{
public class GlobalApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute( "blog", "Archive/{entryDate}",
new {controller = "Archive", action = "Entry"} );
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 order of the routes that you add to the route table is important. Our new custom Blog route is added before the existing Default route. If you reversed the order, then the Default route always will get called instead of the custom route.

The custom Blog route matches any request that starts with /Archive/. So, it matches all of the following URLs:
/Archive/12-25-2009
/Archive/10-6-2004
/Archive/apple
The custom route maps the incoming request to a controller named Archive and invokes the Entry() action. When the Entry() method is called, the entry date is passed as a parameter named entryDate.
You can use the Blog custom route with the controller in Listing 6.
(Listing 6 – ArchiveController.cs )
using System;
using System.Web.Mvc;
namespace MyApp.Controllers

{
    public class ArchiveController : Controller
    {
      public string Entry(DateTime entryDate)
      {
         return "You requested the entry on " + entryDate.ToString();
      }
    }
}
Notice that the Entry() method in Listing 6 accepts a parameter of type DateTime. The MVC framework is smart enough to convert the entry date from the URL into a DateTime value automatically. If the entry date from the URL cannot be converted to a DateTime, an error is raised.

No comments: