Chapter 34

ASP.NET MVC

WHAT’S IN THIS CHAPTER?

WROX.COM CODE DOWNLOADS FOR THIS CHAPTER

Please note that all the code examples in this chapter are available as a part of this chapter’s code download on the book’s website at www.wrox.com on the Download Code tab.

Model-View-Controller (MVC) has been an important architectural pattern in computer science for many years. Originally named Thing-Model-View-Editor in 1979, it was later simplified to Model-View-Controller. It is a powerful and elegant means of separating concerns within an application and applies itself extremely well to web applications. Its explicit separation of concerns does add a small amount of extra complexity to an application’s design, but the extraordinary benefits outweigh the extra effort. It’s been used in dozens of frameworks since its introduction. You can find MVC in Java and C++, on Mac and on Windows, and inside literally dozens of frameworks.

Understanding the core concepts of MVC is critical to using it effectively. This chapter discusses the history of the MVC pattern, as well as how it is used in web programming today.

ASP.NET MVC 1.0 shipped as a downloadable add-on to Visual Studio 2008. ASP.NET MVC 2 shipped built-in with Visual Studio 2010. ASP.NET MVC 3 shipped as a downloadable add-on to Visual Studio 2010. Now in Visual Studio 2012, ASP.NET MVC 4 ships built-in. This chapter also covers some of the limitations of ASP.NET Web Forms and how ASP.NET MVC attempts to release the developer from those limitations.

DEFINING MODEL-VIEW-CONTROLLER

Model-View-Controller (MVC) is an architectural pattern used to separate an application into three main aspects:

This pattern is used frequently in web programming. With ASP.NET MVC, it’s translated roughly to the following:

MVC ON THE WEB TODAY

For many, the web didn’t really become prominent until the first graphical browsers began to flood the market, starting with Mosaic in 1993. Shortly after, dynamic web pages began showing up using languages such as Perl and enabled by technologies like the Common Gateway Interface (CGI). The technology available in the early stages of the web was focused more around the concept of scripting HTML to do light content-driven work, as opposed to deep application logic, which just wasn’t needed back then.

As the web grew and HTML standards began to allow for richer interactions, the notion of the web as an application platform began to take off. In terms of Microsoft, the focus was on quick and simple. In line with that, Active Server Pages (ASP) was born in 1996.

ASP used VBScript, a very simple, lightweight language that gave developers a lot of “un-prescribed freedom” in terms of the applications they could create. A request for an ASP page would be handled by a file with the .asp extension, which consisted of a server-side script intermixed with HTML markup. Written in a procedural language, many ASP pages often devolved into “spaghetti code,” in which the markup was intertwined with code in ways that were difficult to manage and maintain. Although writing clean ASP code was possible, it took a lot of work, and the language and tools were not sufficiently helpful. Even so, although it took a lot of work, ASP did provide full control over the markup produced.

In January of 2002, Microsoft released version 1.0 of the .NET platform, which included the original version of ASP.NET, and thus Web Forms was born. Its birth provided access to more advanced tools and object-oriented languages for building a website.

ASP.NET has grown tremendously over the past 10 years and has made developing web pages very productive and simple by abstracting the repetitive tasks of web development into simple drag-and-drop controls. This abstraction can be a tremendous help, but some developers have found that they want more control over the generated HTML and browser scripting. As the focus on testing has grown, they also want to be able to easily test their web page logic.

As languages matured and web server software grew in capability, MVC soon found its way into web application architectures. But MVC didn’t hit its mainstream stride until July of 2004, when a 24-year-old developer at 37Signals in Chicago, Illinois, named David Heinemeier Hansson, introduced the world to his fresh way of thinking about MVC.

David, or DHH as he’s known in the community, created Ruby on Rails, a web development framework that used the Ruby language and the MVC pattern to create something special.

Now let’s further delve into ASP.NET MVC and answer the question, “Why not Web Forms?”

In February of 2007, Scott Guthrie of Microsoft sketched out the core of ASP.NET MVC while flying on a plane to a conference on the east coast of the United States. It was a simple application, containing a few hundred lines of code, but the promise and potential it offered for parts of the Microsoft web developer audience was huge.

MODEL-VIEW-CONTROLLER AND ASP.NET

ASP.NET MVC relies on many of the same core strategies that the other MVC platforms use, plus it offers the benefits of compiled and managed code and exploits any new language features in the latest version of the .NET Framework. Each of the MVC frameworks used on the web usually share in some fundamental tenets:

Serving Methods, Not Files

Web servers initially served up HTML stored in static files on disk. As dynamic web pages gained prominence, web servers served HTML generated on the fly from dynamic scripts that were also located on disk. With MVC, serving up HTML is a little different. The URL tells the routing mechanism which controller to instantiate and which action method to call and supplies the required arguments to that method. The controller’s method then decides which view to use, and that view then does the rendering.

Rather than having a direct relationship between the URL and a file living on the web server’s hard drive, a relationship exists between the URL and a method on a controller object. ASP.NET MVC implements the “front controller” variant of the MVC pattern, and the controller sits in front of everything except the routing subsystem.

A good way to conceive of the way that MVC works in a web scenario is that MVC serves up the results of method calls, not dynamically generated (also known as scripted) pages. In fact, a speaker once called this “RPC for the Web,” which is particularly apt, although quite a bit narrower in scope.

Is This the Future of Web Forms?

One of the major concerns that we’ve heard when talking to people about ASP.NET MVC is that its release means the death of Web Forms. This just isn’t true. ASP.NET MVC is not ASP.NET Web Forms 4.5. It’s an alternative to Web Forms, and it’s a fully supported part of the framework. While Web Forms continues to march on with new innovations and new developments, ASP.NET MVC will continue as a parallel alternative that’s totally supported by Microsoft.

One interesting way to look at this is to refer to the namespaces in which these technologies live. If you could point to a namespace and say, “That’s where ASP.NET lives,” it would be the System.Web namespace. ASP.NET MVC lives in the System.Web.Mvc namespace. It’s not System.Mvc, and it’s not System.Web2.

ASP.NET MVC has been folded into .NET since version 4 of the framework, and it’s built into Visual Studio 2012 out of the box. This cements ASP.NET MVC’s place as a fundamental part of ASP.NET itself.

Why Not Web Forms?

In ASP.NET Web Forms, you create an instance of System.Web.UI.Page and put “server controls” on it (for example, a calendar and some buttons) so that the user can enter or view information. You then wire these controls to events on the System.Web.UI.Page to allow for interactivity. This page is then compiled, and when it’s called by the ASP.NET run time, a server-side control tree is created, each control in the tree goes through an event life cycle, it renders itself, and the result is served back as HTML. As a result, a new web aesthetic started to emerge, Web Forms layers eventing and state management on top of HTTP, a truly stateless protocol.

Why was this abstraction necessary? Remember that Web Forms was introduced to a Microsoft development community that was very accustomed to Visual Basic 6. Developers using VB6 would drag a button onto the design surface, double-click the button, and a Button_Click event handler method was instantly created for them. This was an incredibly powerful way to create business applications and had everyone excited about Rapid Application Development (RAD) tools. When developers started using classic ASP, it was quite a step backward from the rich environment they were used to in Visual Basic. For better or worse, Web Forms brought that Rapid Application Development experience to the web.

However, as the web matured and more and more people came to terms with their own understanding of HTML as well as the introduction of cascading style sheets (CSS) and XHTML, a new web aesthetic started to emerge. Web Forms is still incredibly productive for developers, enabling them to create a web-based line of business applications very quickly. However, the HTML it generates looks, well, generated, and can sometimes offend the sensibilities of those who handcraft their XHTML and CSS sites. This is especially true with HTML5 and a renewed focus on more semantic markup. Web Forms concepts like ViewState and the Postback event model have their place, but many developers want a lower-level alternative that embraces not only HTML, but also HTTP itself.

Additionally, the architecture of Web Forms also makes testing via the current unit testing tools such as NUnit, MbUnit, and xUnit.NET difficult. ASP.NET Web Forms wasn’t designed with unit testing in mind, and although a number of hacks can be found on the web, it’s fair to say that Web Forms does not lend itself well to test-driven development. ASP.NET MVC offers absolute control over HTML, doesn’t deny the existence of HTTP, and was designed from the ground up with an eye toward testability.

ASP.NET MVC Is Totally Different!

Yes, ASP.NET MVC is totally different. That’s the whole point. It’s built on top of a system of values and architectural principles that is very different from those in Web Forms. ASP.NET MVC values extensibility, testability, and flexibility. It’s very lightweight and doesn’t make a lot of assumptions on how you will use it — aside from the fact that it assumes you appreciate the Model-View-Controller pattern.

Why “(ASP.NET > ASP.NET MVC) == True”

Creating your first MVC application is fairly straightforward. You can use any version of Visual Studio 2012 to create the basic application. Just follow these steps:

1. Open Visual Studio 2012 and select File ⇒ New Project.
2. From the New Project dialog box, shown in Figure 34-1, select ASP.NET MVC 4 Web Application.
3. Pick your project name and where it’s going to live on disk, and click OK. The New ASP.NET MVC 4 Project dialog box appears, as shown in Figure 34-2. The New ASP.NET MVC 4 Project dialog box enables you to select the specific MVC template you would like to use to create your project. Select the Internet Application project template.
4. The New ASP.NET MVC 4 Project dialog box also contains the settings for creating a Unit Test project. Check the box labeled “Create a unit test project” to create a solution that includes both the basic ASP.NET MVC project and an additional Unit Test project. By default, the Test framework drop-down list includes Visual Studio Unit Test as an option. If you’ve installed a third-party unit-testing framework like MbUnit or NUnit, additional options appear in this drop-down.
5. Click OK, and a solution with projects that look like that shown in Figure 34-3 appears. Note that, although this is an ASP.NET application, along with a standard class library, it has some additional folders you haven’t seen before.

In fact, the application has quite a few more directories than you might be used to; this is by design. ASP.NET MVC, like other MVC frameworks, relies heavily on the idea that you can reduce effort and code by relying on some basic structural rules in your application. Ruby on Rails expresses this powerful idea very succinctly: Convention over Configuration.

Convention over Configuration

The concept of “Convention over Configuration” was made popular by Ruby on Rails and essentially means the following: We know, by now, how to build a web application. Let’s roll that experience into the framework so we don’t have to configure absolutely everything again.

You can see this concept at work in ASP.NET MVC by taking a look at the three core directories that make the application work:

You don’t have to set these folder names in the web.config file, they are just expected to be there by convention.

This saves you the work of having to edit an XML file such as your web.config file, for example, to explicitly tell the MVC engine “you can find my controllers in the Controllers directory.” It already knows. It’s convention.

ASP.NET MVC’s conventions are straightforward. This is what is expected of your application’s structure:

If you take a deeper, expanded look at the initial structure of the sample application, shown in Figure 34-4, you can see these conventions at work.

FIGURE 34-4

image

Two controllers, HomeController and AccountController, are in the Controllers directory, and a number of views are in the Views directory. The following discussion focuses on the views under /Views/Home named About and Index.

Although no convention is expected of you with respect to what you name your views, you can lean on the ASP.NET MVC convention that you give your view the same name as your action. Using this convention also makes reviewing and understanding your application easier for other developers.

You can see this convention in action in the way that the template creates the Index and About views. These are also the names of the controller actions that are called, and the code, shown in C#, to render these views is simply:

return View();

That can be a little confusing. You can see a clear example by changing the application a little and then digging in:

1. Open HomeController.cs or HomeController.vb, copy and paste the About method, and create a duplication called Foo, as shown here:

VB

    Function Foo() As ActionResult
        ViewData("Message") = "Foo page."
 
        Return View()
    End Function

C#

        public ActionResult Foo()
        {
            ViewBag.Message = "Foo page.";
 
            return View();
        }
2. Having made this one small addition, start debugging your application. The ASP.NET Development Server automatically selects a high port number and your browser launches. Your browser ends up navigating to an address like http://localhost:3098, as shown in Figure 34-5.
3. See how there’s no .aspx extension? ASP.NET MVC puts you in full control. Now, change the relative URL in your browser’s address bar from / to /Home/Foo. Things get interesting, as shown in Figure 34-6. Remember that you’re just returning the result of the call to View in your Foo method. Because you’re in the Foo method of HomeController, the system is looking for a View called Foo in a number of locations. ASP.NET MVC is smart enough to give you an error message that you can actually do something useful with. It’s really quite refreshing!
System.InvalidOperationException: The view 'Foo' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Home/Foo.aspx
~/Views/Home/Foo.ascx
~/Views/Shared/Foo.aspx
~/Views/Shared/Foo.ascx
~/Views/Home/Foo.cshtml
~/Views/Home/Foo.vbhtml
~/Views/Shared/Foo.cshtml
~/Views/Shared/Foo.vbhtml
The error message lists (see Figure 34-6) the locations where the system looked for Views, in the order searched. It gives you enough information to infer the naming convention for Views.
First, it looks in a directory under /Views with the name of the current controller, in this case Home, then it looks in /Views/Shared. The RazorViewEngine that ASP.NET MVC 4 uses by default looks for .aspx pages, then .ascx files, then .cshtml and .vbhtml files.
4. Go back into the HomeController and change the call to View in the Foo method to include the name of a View as a parameter:

VB

    Function Foo() As ActionResult
        ViewData("Message") = "Foo page."
 
        Return View("Index")
    End Function

C#

        public ActionResult Foo()
        {
            ViewBag.Message = "Foo page.";
 
            return View("Index");
        }
5. Start your application again, and visit /Home/Foo again. The Index view is rendered, and the Message string from the Foo action appears in the header of the page.
6. Switch back over to Visual Studio and set a breakpoint on the line that returns the result of View. Refresh your browser, confirming that you’re still at /Home/Foo, and get ready to dig in.

The Third Request Is the Charm

Take a moment and think about what’s happening here. What’s the state of affairs within your application? Your instance of Visual Studio should look more or less like Figure 34-7.

FIGURE 34-7

image

Spend a moment looking at Visual Studio (or the figure, if you like) and try to determine what it is telling you. How did you get here? Where are you?

You visited /Home/Foo in the browser, and now you’re magically sitting on a breakpoint inside of the Foo action method. The Call Stack tool window confirms this, but doesn’t tell you enough. How did you get here? Right-click the whitespace of the call stack, and select Show External Code. You might also drag the Call Stack tool window and “tear it off” Visual Studio to better analyze the crush of information that’s going to be revealed. If you have multiple monitors, remember that Visual Studio 2012 supports that, so you can fill one whole monitor with the Call Stack tool window if you like.

The Call Stack tool window contains so much information, in fact, that some of the important bits and objects have been highlighted, as shown in the snippet in Figure 34-8. Remember that call stacks are read from bottom to top, where the bottom is where you started and the top is the line you are currently debugging. In this call stack, some parts are more significant than others.

FIGURE 34-8

image

Starting at the bottom, you can see that the request is being handled by ASP.NET — specifically, by System.Web.HttpRuntime. This is the “beginning” of ASP.NET. Note that this is System.Web, and you’re inside System.Web.dll — nothing MVC specific has happened yet. If you’re already familiar with Web Forms, you might find it useful to remember what ASP.NET proper is, where it ends, and where ASP.NET MVC starts.

The first significant thing happens (remember, you’re reading from bottom to top) in the bottom callout shown in Figure 34-8. What can you learn from the highlighted transition from ASP.NET to ASP.NET MVC? ASP.NET MVC is built on ASP.NET with HttpHandlers and HttpModules. That’s where MVC gets its hooks in.

The fact that ASP.NET MVC is implemented as an HttpHandler is comforting because you know that the team “played by the rules” when writing it. No internal knowledge or secrets exist in the design of ASP.NET MVC. It’s written using the same public constructs and APIs that are available to all developers.


NOTE We find great comfort in the discovery that ASP.NET MVC has no secrets in its design. Less magic in ASP.NET MVC means we can understand it more easily. If ASP.NET MVC is an HttpHandler, and you’ve written lots of those, then it’s less magical than you thought! It’s also nice to see that ASP.NET itself was flexible and extensible enough to allow something like ASP.NET MVC to be created.

Another thing you can glean from these discoveries is that because ASP.NET MVC uses HttpHandlers (and HttpModules) to do its work, MVC is built on ASP.NET. This might seem like an obvious statement to some, but a very common question is “Is ASP.NET MVC a whole new ASP.NET?” You can see from Figure 34-8 that it’s not. It’s built on the same infrastructure, the same “core” ASP.NET, that you’ve used for years.

Glance back at Figure 34-8 and look at that call stack. Remember that you’re currently sitting on a breakpoint in the Foo method inside HomeController. Who created HomeController? Someone had to “new it up.” Who called Foo for you?

Inside the MvcHandler’s ProcessRequest method an instance of a controller is created by the DefaultControllerFactory. DefaultControllerFactory is the built-in factory created and used by ASP.NET MVC to search and create controllers. ASP.NET MVC creates an instance of the HomeController and then calls the Execute method on the controller. This method, in turn, relies on the controller’s action invoker (by default a ControllerActionInvoker) to actually call the method.

Remember that you opened a browser and requested /Home/Foo. The ASP.NET MVC application routed the request to an MvcHandler. That Handler created an instance of the HomeController and called the Foo method. ASP.NET MVC handled both object activation and method invocation for you.

The /Home/Foo URL was intercepted by the UrlRoutingModule. That module is responsible for making sure the right URLs go to the right controllers by parsing the URLs and creating some routing data. The MVC pipeline uses a ControllerFactory and a ControllerActionInvoker to create your controller and call its method, respectively.

Controllers exist to “do stuff.” What that stuff is, is up to you. They talk to a model, do calculations, whatever. However, they don’t render HTML, and they don’t talk to databases. That’s separation of concerns. Controllers are concerned with controlling.

The controller passes ViewData to a view, which is concerned with rendering HTML (or whatever you want). That HTML contains links to other URLs, and the cycle continues.

UNDERSTANDING ROUTES AND URLS

Software developers are well known for paying close attention to the little details, especially when it comes to the quality and structure of their source code. They often fight long battles over code indentation styles and where curly braces should go. So it comes as a bit of a surprise when you approach a majority of sites built using ASP.NET and encounter a URL that looks like this:

http://example.com/products/list.aspx?id=17313&catid=33723&page=3

For all the attention developers pay to code, why not pay the same amount of attention to the URL? It may not seem all that important, but the URL is a legitimate and widely used web user interface.

Usability expert Jakob Nielsen (www.nngroup.com) urges developers to pay attention to URLs and provides the following guidelines for high-quality URLs:

Traditionally, in many web frameworks such as classic ASP, JSP, PHP, ASP.NET, and the like, the URL represents a physical file on disk. For example, when you see a request for

http://example.com/products/list.aspx

you could bet your kid’s tuition that the website has a directory structure that contains a Products folder and a List.aspx file within that folder. In this case, a direct relationship exists between the URL and what physically exists on disk. When such a request is received by the web server, the web framework executes code associated with this file to respond to the request. In many cases, this code contains or is associated with a template that intermixes server-side declarations with HTML markup to generate the resulting markup sent back to the browser via the response.

Routing within the ASP.NET MVC Framework serves two main purposes:

Now that you understand something of URLs and routing, it’s time to take a closer look at routing and how it’s different from URL rewriting.

Routing Compared to URL Rewriting

To better understand routing, many developers compare it to URL rewriting. After all, both approaches are useful in creating a separation between the URL and the code that handles the URL, which can help create “pretty” URLs for search engine optimization (SEO) purposes. One key difference, though, is that URL rewriting represents a “page-centric” view of URLs. Most rewriting schemes with ASP.NET rewrite a URL for one page to be handled by another. For example, you might see

/product/bolts.aspx

rewritten as

/product/display.aspx?productid=111

Routing, on the other hand, takes a “resource-centric” view of URLs. In this case, the URL represents a resource (not necessarily a page) on the web. With ASP.NET routing, this resource is a piece of code that executes when the incoming request matches the route. The route determines how the request is dispatched based on the characteristics of the URL — it doesn’t rewrite the URL.

Another key difference is that routing also helps generate URLs using the same mapping rules that it uses to match incoming URLs. Another way to look at it is that ASP.NET routing is more like bidirectional URL rewriting. Where this comparison falls short is that ASP.NET routing never actually rewrites your URL. The request URL that the user makes in the browser is the same URL your application sees throughout the entire request life cycle.

Defining Routes

Every ASP.NET MVC application needs at least one route to define how the application should handle requests, but usually ends up with at least a handful. Conceivably, a very complex application could have dozens of routes or more.

In this section, you look at how to define routes. Route definitions start with the URL, which specifies a pattern that the route will match. Along with the route URL, routes can also specify default values and constraints for the various parts of the URL, providing tight control over how the route matches incoming request URLs.

You start with an extremely simple route and build up from there.

Setting Route URLs

After you create a new ASP.NET MVC Web Application project, take a quick look at the code in Global.asax.cs. You’ll notice that the Application_Start method contains a call to a static method named RegisterRoutes in the RouteConfig class. This method is where all routes for the application are registered.


NOTE Instead of calling the configuration and setup methods directly in the Global.asax, they have been placed in static methods in classes located in the App_Start ASP.NET folder. This not only keeps the Global.asax cleaner, but enforces separation of concerns.

Clear out the routes in the RegisterRoutes method of the RouteConfig class for now and replace them with this very simple route (shown in C#):

routes.MapRoute("simple", "{first}/{second}/{third}");

The simplest form of the MapRoute method takes in a name for the route and the URL pattern for the route. The name is discussed a bit later in this section. For now, focus on the URL pattern.

Notice that the route URL consists of several URL segments (a segment is everything between slashes but not including the slashes), each of which contains a placeholder delimited using curly braces. These placeholders are referred to as URL parameters. This structure is a pattern-matching rule used to determine whether this route applies to an incoming request. In this example, this rule will match any URL with three segments because a URL parameter, by default, matches any nonempty value. When it matches a URL with three segments, the text in the first segment of that URL corresponds to the {first} URL parameter, the value in the second segment of that URL corresponds to the {second} URL parameter, and the value in the third segment corresponds to the {third} parameter.

You can name these parameters anything you want, as in this case. When a request comes in, routing parses the request URL into a dictionary (specifically a RouteValueDictionary accessible via the RouteData in the RequestContext), using the URL parameter names as the keys and subsections of the URL in the corresponding position as the values. When using routes in the context of an MVC application, certain parameter names carry a special purpose. Table 34-1 displays how the route just defined converts certain URLs into a RouteValueDictionary.

TABLE 34-1

URL URL PARAMETER VALUES
/products/display/123 {first} = products
{second} = display
{third} = 123
/foo/bar/baz {first} = foo
{second} = bar
{third} = baz
/a.b/c-d/e-f {first} = "a.b"
{second} = "c-d"
{third} = "e-f"

If you actually make a request to the URLs listed in the preceding table, you may notice that your ASP.NET MVC application appears to be broken. Although you can define a route with any parameter names you want, certain special parameter names are required by ASP.NET MVC for the route to function correctly: {controller} and {action}.

The value of the {controller} parameter is used to instantiate a controller class to handle the request. By convention, MVC appends the suffix Controller to the {controller} value and attempts to locate a type of that name (case insensitively) that also inherits from the System.Web.Mvc.IController interface.

Going back to the simple route example (VB example shown), change it from

routes.MapRoute("simple", "{first}/{second}/{third}")

to

routes.MapRoute("simple", "{controller}/{action}/{id}")

so that it contains the special URL parameter names.

Now looking again at the first example in Table 34-1, you see that the request for /products/display/123 is a request for a {controller} named "Products". ASP.NET MVC takes that value and appends the Controller suffix to get a type name, ProductsController. If a type of that name that implements the IController interface exists, it is instantiated and used to handle the request.

The {action} parameter value is used to indicate which method of the controller to call to handle the current request. Note that this method invocation applies only to controller classes that inherit from the System.Web.Mvc.Controller base class. Continuing with the example of /products/display/123, the method of ProductsController that MVC will invoke is Display or display, because it is case insensitive.

Note that the third URL in Table 34-1, although it is a valid route URL, will probably not match any real controller and action, because it would attempt to instantiate a controller named a.bController and call the method named c-d, which is not a valid method name.

Any route parameters other than {controller} and {action} are passed as parameters to the action method, if they exist. Listing 34-1 demonstrates a products controller with a single action (Controllers/ProductsController.cs and Controllers/ProductsController.vb in the code download for this chapter).

LISTING 34-1: A products controller with a single action

VB

Public Class ProductsController
    Inherits System.Web.Mvc.Controller
 
    Function Display(id As Integer) As ActionResult
        'Do something
        Return View()
    End Function
End Class

C#

using System.Web.Mvc;
 
public class ProductsController : Controller
{
  public ActionResult Display(int id)
  {
    //Do something
    return View();
  }
}

A request for /products/display/123 would cause MVC to instantiate this class and call the Display method, passing in 123 for the id.

In the previous example with the route URL {controller}/{action}/{id}, each segment contains a URL parameter that takes up the entire segment. This doesn’t have to be the case. Route URLs do allow for literal values within the segments. For example, if you are integrating MVC into an existing site and want all your MVC requests to be prefaced with the word site, you could do it as follows:

site/{controller}/{action}/{id}

This indicates that the first segment of a URL must start with site to match this request. Thus, /site/products/display/123 matches this route, but /products/display/123 does not match.

Having URL segments that intermix literals with parameters is even possible. The only restriction is that two consecutive URL parameters are not allowed. Thus

{language}-{country}/{controller}/{action}
{controller}.{action}.{id}

are valid route URLs, but

{controller}{action}/{id}

is not a valid route. No way exists for the route to know when the controller part of the incoming request URL ends and when the action part should begin.

Looking at some other samples (shown in Table 34-2) can help you see how the URL pattern corresponds to matching URLs.

TABLE 34-2

ROUTE URL PATTERN EXAMPLES OF URLS THAT MATCH
{controller}/{action}/{category} /products/list/beverages/blog/posts/123
service/{action}-{format} /service/display-xml
{reporttype}/{year}/{month}/{date} /sales/2008/1/23

Under the Hood: How Routes Tie Your URL to an Action

You have walked through how routes map to controller actions within the MVC Framework. Now you take a look under the hood to get a better look at how this happens and get a better picture of where the dividing line is between routing and MVC.

One common misconception is that routing is just a feature of ASP.NET MVC. During the early stages of ASP.NET MVC implementation, this was true, but after a while, it became apparent that this was a more generally useful feature. The ASP.NET Dynamic Data team in particular was also interested in using routing inside Dynamic Data itself. At that point, routing became a more general-purpose feature that has neither internal knowledge of nor dependency on MVC.

One very outward bit of proof that routing is separate is not just that it’s a separate assembly, but that it lives in the System.Web.Routing namespace, and not a theoretical System.Web.Mvc.Routing. You can glean a lot of information from reading into namespaces.


NOTE The discussion here focuses on routing for IIS 7 integrated mode and IIS 8. Some slight differences exist when using routing with IIS 7 classic mode or IIS 6. When you are using the Visual Studio 2012 built-in web server, the behavior is very similar to IIS 8.

The High-Level Request Routing Pipeline

All this talk about routing might be a lot of information for you to process. However, it’s important to understand because routing really is your most powerful tool to control your application’s URLs.

Broken down into its component parts, the routing pipeline consists of the following five high-level steps:

1. UrlRoutingModule attempts to match the current request with the routes registered in the RouteTable.
2. If a route matches, the routing module grabs the IRouteHandler from that route.
3. The routing module calls GetHandler from the IRouteHandler, which returns an IHttpHandler. Recall that a typical ASP.NET page (also known as System.Web.UI.Page) is nothing more than an IHttpHandler.
4. ProcessRequest is called on the HttpHandler, thus handing off the request to be handled.
5. In the case of MVC, the IRouteHandler is by default an instance of MvcRouteHandler, which in turn returns an MvcHandler (implement IHttpHandler). The MvcHandler is responsible for instantiating the correct controller and calling the action method on that controller.

Route Matching

At its core, routing is simply matching requests and extracting route data from that request and passing it to an IRouteHandler. The algorithm for route matching is very simple from a high-level perspective.

When a request comes in, the UrlRoutingModule iterates through each route in the RouteCollection accessed via RouteTable.Routes in order. It then asks each route, “Can you handle this request?” If the route answers “Yes I can!”, then the route lookup is done and that route gets to handle the request.

The question of whether a route can handle a request is asked by calling the method GetRouteData. The method returns null if the current request is not a match for the route (in other words, no real conversation is going on between the module and routes).

RouteData

Recall that when you call GetRouteData, it returns an instance of RouteData. What exactly is RouteData. RouteData contains information about the route that matched a particular request, including context information for the specific request that matched.

Recall that the previous section showed a route with the following URL: {foo}/{bar}/{baz}. When a request for /products/display/123 comes in, the route attempts to match the request. If it does match, it then creates a dictionary that contains information parsed from the URL. Specifically, it adds a key to the dictionary for each url parameter in the route URL.

So, in the case of {foo}/{bar}/{baz}, you would expect the dictionary to contain at least three keys: "foo", "bar", and "baz". In the case of /products/display/123, the URL is used to supply values for these dictionary keys. In this case, foo = products, bar = list, and baz = 123.

Parameter Defaults and Optional Parameters

In some cases, you may want to specify a default value for a parameter. You can specify default values for parameters when you register your route. You can even specify defaults for the special parameter names {controller} and {action}. To set default values for a route, you use an override of the MapRoute method, assigning the Defaults property of the Route class. The default route when you create a new MVC app specifies default values for both the controller and the action.


NOTE If you changed the default route earlier in the chapter, you should change it back to this default route value that is used in the remaining examples.

VB

routes.MapRoute( _
   name:="Default", _
   url:="{controller}/{action}/{id}", _
   defaults:=New With {.controller = "Home", .action = "Index", _
       .id = UrlParameter.Optional} _
)

C#

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index",
        id = UrlParameter.Optional }
);

You may also notice in the default mapping the use of UrlParameter.Optional for the id parameter. In certain situations, you may want to specify that a parameter does not have a default and that it is optional. You do this by specifying the default value for the parameter using a special value, UrlParameter.Optional. If you want to access this parameter in your action, declare the parameter as a Nullable type. Listing 34-2 shows the result of modifying Listing 34-1 to accept an optional parameter (Controllers/ProductsController.cs and Controllers/ProductsController.vb in the code download for this chapter).

LISTING 34-2: A products controller action with an optional parameter

VB

Public Class ProductsController
    Inherits System.Web.Mvc.Controller
 
    Function Display(id As Integer?) As ActionResult
        'Do something
        Return View()
    End Function
End Class

C#

using System.Web.Mvc;
 
public class ProductsController : Controller
{
  public ActionResult Display(int? id)
  {
    //Do something
    return View();
  }
}

CONTROLLERS

You might want to remember a quick definition: Controllers within the MVC pattern are responsible for responding to user input, often making changes to the model in response to user input.

In this way, controllers in the MVC pattern are concerned with the flow of the application, working with data coming in, and providing data going out to the relevant view.

Defining the Controller: The IController Interface

Among the core focuses of ASP.NET MVC are extensibility and flexibility. When building software with these goals in mind, leveraging abstraction as much as possible by using interfaces is important.

For a class to be a controller in ASP.NET MVC, it must at minimum implement the IController interface, and by convention the name of the class must end with the suffix Controller. The naming convention is actually quite important — and you’ll find that many of these small rules are in play with ASP.NET MVC, which will make your life just a little bit easier by not making you define configuration settings and attributes. Ironically, the IController interface is quite simple given the power it is abstracting:

public interface IController
{
  void Execute(RequestContext requestContext);
}

Creating an IController is a simple process, really. When a request comes in, the routing system identifies a controller, and it calls the Execute method.

The ControllerBase class is an abstract base class that layers a bit more API surface on top of the IController interface. It provides the TempData, ViewData, and ViewBag properties. The Execute method of ControllerBase is responsible for creating the ControllerContext, which provides the MVC-specific context for the current request much the same way that an instance of HttpContext provides the context for ASP.NET in general (providing request and response, URL, and server information, among other elements).

This base class is still very lightweight and enables developers to provide extremely customized implementations for their own controllers, while benefiting from the action filter infrastructure in ASP.NET MVC. What it doesn’t provide is the ability to convert actions into method calls. That’s where the Controller class comes in.

The Controller Class and Actions

In theory, you could build an entire site with classes that simply implement ControllerBase or IController, and it would work. Routing would look for an IController by name and then call Execute, and you would have yourself a very, very basic website.

This approach, however, is akin to working with ASP.NET using raw HttpHandlers — it would work, but you’re left to reinvent the wheel and plumb the core framework logic yourself. Interestingly, ASP.NET MVC itself is layered on top of HTTP handlers, and overall there was no need to make internal plumbing changes to ASP.NET to implement MVC. Instead, the ASP.NET MVC team simply layered this framework on top of existing ASP.NET extensibility points. The standard approach to writing a controller is to have it inherit from the System.Web.Mvc.Controller abstract base class, which implements the ControllerBase base class. The Controller class is intended to serve as the base class for all controllers, because it provides a lot of nice behaviors to controllers that derive from it.

Now walk through another simple controller example, but this time, add a public method. Using the same project you have been working with previously, create a new controller by right-clicking the Controllers folder and selecting Add ⇒ Controller and then name it Simple2Controller. Listing 34-3 shows the Simple2Controller after adding the necessary code (Controllers/Simple2Controller.vb and Controllers/Simple2Controller.cs in the code download for this chapter).

LISTING 34-3: Simple2Controller class

VB

    Public Class Simple2Controller
        Inherits System.Web.Mvc.Controller
 
        Sub Hello()
            Response.Write("<h1>Hello World Again!</h1>")
        End Sub
 
    End Class

C#

using System.Web.Mvc;
 
    public class Simple2Controller : Controller
    {
        public void Hello()
        {
            Response.Write("<h1>Hello World Again!</h1>");
        }
    }

Press Ctrl+F5 (or Debug ⇒ Run) and navigate to /Simple2/Hello in the browser. Figure 34-9 shows the result.

FIGURE 34-9

image

As before, this is not exactly breathtaking, but it is a bit more interesting. Notice that the URL in the address bar directly correlates to the action method of your controller. If you recall from earlier, the default route for MVC breaks URLs into three main components: /{controller}/{action}/{id}.

Take a look at how that applies to this example. The Simple2 portion of the URL corresponds to the controller name. The MVC Framework appends the Controller suffix to the controller name and locates your controller class, Simple2Controller:

/Simple2/Hello

The last portion of the URL corresponds to the action. The framework locates a public method with this name and attempts to call the method.

Working with Parameters

You can add any number of public methods (which are called actions from here on out to keep with convention) to a controller class, which will all be callable via this pattern. Actions may also contain parameters. Going back to the Simple2Controller, add a new action method that takes in a parameter. Listing 34-4 shows the Simple2Controller after adding a Goodbye action (Controllers/Simple2Controller.vb and Controllers/Simple2Controller.cs in the code download for this chapter).

LISTING 34-4: Simple2Controller with added action

VB

Public Class Simple2Controller
    Inherits System.Web.Mvc.Controller
 
    Sub Hello()
        Response.Write("<h1>Hello World Again!</h1>")
    End Sub
 
    Sub Goodbye(name As String)
        Response.Write("Goodbye " + HttpUtility.HtmlEncode(name))
    End Sub
 
End Class

C#

using System.Web;
using System.Web.Mvc;
 
    public class Simple2Controller : Controller
    {
        public void Hello()
        {
            Response.Write("<h1>Hello World Again!</h1>");
        }
 
        public void Goodbye(string name)
        {
            Response.Write("Goodbye " + HttpUtility.HtmlEncode(name));
        }
    }

This method is callable via the URL:

/Simple2/Goodbye?name=World

Notice that you can pass in parameters to an action method by name via the query string. You can also pass in parameters via the URL segments, discoverable by position as defined in your routes. For example, the following URL is more aesthetically pleasing to many developers and Internet users:

/Simple2/Goodbye/World

Working with parameters passed by URL segment requires you to define how routing will identify these parameters in the URL. Fortunately, the default route (created for you when you click File ⇒ New) is already set up for you and contains a common URL pattern:

{controller}/{action}/{id}.

Changing the action method signature in the Simple2Controller a little bit (by renaming the parameter “name” to “id”) as shown in C#:

    public void Goodbye(string id)
    {
        Response.Write("Goodbye " + HttpUtility.HtmlEncode(id));
    }

enables you to call that method using the “cleaner” URL, and routing will pass the parameter by structured URL instead of a query string parameter:

/Simple2/Goodbye/World

Working with Multiple Parameters

What if you have a method with more than one parameter? This scenario is very common, and rest assured that you can still use query strings, but if you want to pass both parameters via the URL segments, you must define a new route specifically for this situation.

For example, suppose that you have an action method in C# that calculates the distance between two points:

public void Distance(int x1, int y1, int x2, int y2)
{
    double xSquared = Math.Pow(x2 - x1, 2);
    double ySquared = Math.Pow(y2 - y1, 2);
    Response.Write(Math.Sqrt(xSquared + ySquared));
}

Using only the default route, the request would need to look like this:

/Simple2/Distance?x2=1&y2=2&x1=0&y1=0

You can improve on this situation a bit by defining a route that enables you to specify the parameters in a cleaner format. If you remember from the section in this chapter on routing, it was mentioned that you could specify multiple parameters in a segment if you separated them with a constant. This is a good time for an example of that! In the RegisterRoutes static method within the RouteConfig.cs or RouteConfig.vb file, you can use the MapRoute method to define the new route:

routes.MapRoute("distance",
    "Simple2/Distance/{x1},{y1}/{x2},{y2}",
    new { Controller = "Simple2", action = "Distance" }
);

Notice that you are using the comma character to separate x and y coordinates. Now this action method is callable via the URL:

/Simple2/Distance/0,0/1,2

The presence of commas in a URL might look strange, but routing is quite powerful!

So far you’ve used Response.Write in these little example methods, but this violates the principle of separation of concerns. It’s really not the business of a controller to be managing the “views” of your data. That’s something better handled by the “V” in MVC; that is, views.

VIEWS

The view is responsible for providing the user interface (UI) to the user. It is given a reference to the model, and it transforms that model into a format ready to be presented to the user. In ASP.NET MVC, this consists of examining the ViewDataDictionary handed off to it by the controller (accessed via the ViewData or ViewBag property) and transforming that to HTML. Note that ViewBag is a wrapper around the ViewData object that enables you to create dynamic properties for the ViewBag instead of using “magic” strings as keys in the ViewData object.

In the strongly typed view case, which is covered in more depth in the section “Strongly Typed Views” later in the chapter, the ViewDataDictionary has a strongly typed Model object that the view renders. This model might represent the actual domain object, such as a Product instance, or it might be a presentation model object specific to the view, such as a ProductEditViewData instance.

Take a quick look at an example of a view. The following code sample shows the Index view within the C# version of the default ASP.NET MVC project template:

@{
    ViewBag.Title = "Home Page";
}
@section featured {
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h1>@ViewBag.Title.</h1>
                <h2>@ViewBag.Message</h2>
            </hgroup>
            <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>.
                The page features <mark>videos, tutorials, and samples</mark> to
                    help you get the most from ASP.NET MVC.
                If you have any questions about ASP.NET MVC visit
                <a href="http://forums.asp.net/1146.aspx/1?MVC"
                    title="ASP.NET MVC Forum">our forums</a>.
            </p>
        </div>
    </section>
}
<h3>We suggest the following:</h3>
<ol class="round">
    <li class="one">
        <h5>Getting Started</h5>
        ASP.NET MVC gives you a powerful, patterns-based way to build dynamic
        websites that enables a clean separation of concerns and that gives you 
        full control over markup for enjoyable, agile development. ASP.NET MVC 
        includes many features that enable fast, TDD-friendly development for 
        creating sophisticated applications that usethe latest web standards.
        <a href="http://go.microsoft.com/fwlink/?LinkId=245151">Learn more . . . </a>
    </li>
 
    <li class="two">
        <h5>Add NuGet packages and jump-start your coding</h5>
        NuGet makes it easy to install and update free libraries and tools.
        <a href="http://go.microsoft.com/fwlink/?LinkId=245153">Learn more . . . </a>
    </li>
 
    <li class="three">
        <h5>Find Web Hosting</h5>
        You can easily find a web hosting company that offers the right mix of 
        Features and price for your applications.
        <a href="http://go.microsoft.com/fwlink/?LinkId=245157">Learn more . . . </a>
    </li>
</ol>

This is an extremely simple example of a view, but it’s useful for pointing out some of the key details of views in ASP.NET MVC. One of the first things you may notice is the use of @{ and } used to delimit certain blocks of text. These characters are used to define a code block to the Razor view engine, which is the default view engine used to render ASP.NET MVC views. You will see more of this Razor syntax later in the chapter. One of the additional things you’ll notice is that some of the standard HTML page elements are missing. There are no doctype, html, head, or body elements. Much like master pages in Web Forms, Layouts in ASP.NET MVC enables you to define a common template for the site that can be inherited by all of the views on your site. Layouts are covered a little later in this chapter.

If you are familiar with the page directive on Web Forms, you will also notice there is no page directive. That is because this is not a Web Form. In fact, this view is not even rendered using the WebFormViewEngine. ASP.NET MVC enables you to swap out different view engines, but the default view Engine is RazorViewEngine.

Views in ASP.NET MVC derive from either the System.Web.Mvc.ViewPage, which itself derives from System.Web.UI.Page (in the case of the WebFormViewEngine) or the System.Web.Mvc.WebViewPage, which derives from System.Web.WebPages.WebPageBase (in the case of the RazorViewEngine). Strongly typed views derive from the generic ViewPage<T> or WebViewPage<T>, again depending on the view engine.

In keeping with the principle of separation of concerns, views should not contain application and business logic. In fact, they should contain as little code as possible. Although it’s perfectly acceptable for a view to contain view logic, views are generally the most difficult part of the application to test in an automated fashion, and they, therefore, benefit from having very little code.

Specifying a View

So far, this chapter has discussed what a view does and doesn’t do, but it hasn’t addressed how to specify the view that should render the output for a specific action. It turns out that this task is very easy when you follow the conventions implicit in the framework.

When you create a new project template, notice that the project contains a Views directory structured in a very specific manner (see Figure 34-10).

FIGURE 34-10

image

By convention, the Views directory contains a folder per controller, with the same name as the controller, sans the Controller suffix. Within each Controller folder, there’s a view file for each action method, named the same as the action method. This provides the basis for how views are associated to an action method.

For example, an action method can return a ViewResult via the View method like so:

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

This method ought to look familiar; it’s the Index action method of HomeController in the default project template. Because the view name was not specified, the ViewResult returned by this method looks for a view named the same as the action name in the /Views/ControllerName directory. The view selected in this case would be /Views/Home/Index.cshtml or /Views/Home/Index.vbhtml.

As with most things in ASP.NET MVC, this convention can be overridden. Suppose that you want the Index action to render a different view. You could supply a different view name like so:

public ActionResult Index()
{
    ViewBag.Title = "Home Page";
    ViewBag.Message = "Welcome to ASP.NET MVC!";
    return View("NotIndex");
}

In this case, it will still look in the /Views/Home directory, but choose NotIndex.cshtml or NotIndex.vbhtml as the view. In some situations, you might even want to specify a view in a completely different directory structure.

You can use the tilde syntax to provide the full path to the view like so:

public ActionResult Index()
{
    ViewBag.Title = "Home Page";
    ViewBag.Message = "Welcome to ASP.NET MVC!";
    return View("~/Some/Other/View.cshtml");
}

When using the tilde syntax, you must supply the file extension of the view because this bypasses the view engine’s internal lookup mechanism for finding views.

ASP.NET MVC Layouts

You typically want to create a similar look and feel to your entire site, or at least to various sections of your site. ASP.NET 2.0 introduced the concept of “master pages” to accomplish this when using .aspx pages in a typical Web Forms application. Razor supports a feature called Layouts that supports this same concept in Razor views.

The easiest way to explore layouts in Razor views is to look at the default project created earlier in the chapter. If you review Figure 34-10, you will see a folder located under the Views folder that has no corresponding controller, the Shared folder. While most of the views are located in folders corresponding to the controller that provides the data to them, the views in the Shared folder are meant to be “shared” by more than one view. You will also note that many of the views in the Shared folder follow a naming convention having a leading underscore. This convention is carried over from ASP.NET Web Pages, which doesn’t have the same sort of protection built in regarding Views folders and routing that you get within MVC. Layout pages are not intended to be served and browsed directly. The Web Pages framework has been configured not to allow a file with a leading underscore in its name to be requested directly. The following code sample shows the C# version of the layout file:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@ViewBag.Title - My ASP.NET MVC Application</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <meta name="viewport" content="width=device-width" />
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body>
        <header>
            <div class="content-wrapper">
                <div class="float-left">
                    <p class="site-title">
                        @Html.ActionLink("your logo here", "Index", "Home")
                    </p>
                </div>
                <div class="float-right">
                    <section id="login">
                        @Html.Partial("_LoginPartial")
                    </section>
                    <nav>
                        <ul id="menu">
                            <li>@Html.ActionLink("Home", "Index", "Home")</li>
                            <li>@Html.ActionLink("About", "About", "Home")</li>
                            <li>@Html.ActionLink("Contact", "Contact", 
                                "Home")</li>
                        </ul>
                    </nav>
                </div>
            </div>
        </header>
        <div id="body">
            @RenderSection("featured", required: false)
            <section class="content-wrapper main-content clear-fix">
                @RenderBody()
            </section>
        </div>
        <footer>
            <div class="content-wrapper">
                <div class="float-left">
                    <p>&copy; @DateTime.Now.Year - My ASP.NET MVC Application</p>
                </div>
            </div>
        </footer>
 
        @Scripts.Render("~/bundles/jquery")
        @RenderSection("scripts", required: false)
    </body>
</html>

The first thing you should notice is the doctype, html, head, and body tags that were mentioned as missing when first looking at the Index view. This layout file is inherited, by default, by all the views in the site. The next important thing to note is the call to @RenderBody(). The views that are based on this layout file insert their content where this call is made.

A view can specify that it should use a layout page by setting the Layout property on the WebPageBase class. You can do this in the view itself using a Razor code block as shown here:

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

This property must be set on all views that want to use a layout. You can have multiple layout files for a site and specify the particular layout file to be used on each view by simply changing the name of the layout file in that view. Setting the Layout property on all of your views would require a lot of duplicated code. To prevent so much duplication, you can use another feature of Razor that enables you to define layout logic for all views in a single location. This not only cuts down on duplication, but makes your code more maintainable. You can add a file called _ViewStart.cshtml or _ViewStart.vbhtml to the Views folder. The _ViewStart file can be used to define common view code that will execute at the start of the rendering of each view. You can move the setting of the Layout property from the view to the _ViewStart file and it will be applied to all views. You can override this in any view simply by setting the Layout property to a different layout in that view. In the default template, the _ViewStart file contains the code block shown in the preceding code setting the Layout property. You can specify the layout to be used in a view when creating the view using the Add View dialog box in Visual Studio. Figure 34-11 shows the Add View dialog box with the “Use a layout or master page” option checked. Notice that you can leave the layout filename field blank if you are using the _ViewStart file.

FIGURE 34-11

image

Another important thing you will see in the layout file are calls to the @RenderSection method. So far you have only looked at the main body section of the layout file rendered using the @RenderBody method. Razor also supports created multiple “named sections” that can be used to render dynamic content to multiple, non-contiguous, regions of the final response. The first parameter to the @RenderSection method is the name of the section. This name is used to specify the content of the section in the view. You can specify a section as required or optional by setting the required parameter. Look again at the layout file code snippet shown earlier. You will see a named section called “featured” rendered by the call to @RenderSection:

@RenderSection("featured", required: false)

Now look at the Index view in the Views/Home folder. The view contains a section called “featured.” No matter where the “featured” section is in the view file, it will be displayed at the point in the layout where the corresponding @RenderSection is called. The following code sample shows the section block in the Index view:

@section featured {
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h1>@ViewBag.Title.</h1>
                <h2>@ViewBag.Message</h2>
            </hgroup>
            <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>.
                The page features <mark>videos, tutorials, and samples</mark> to 
                help you get the most from ASP.NET MVC.
                If you have any questions about ASP.NET MVC visit
                <a href="http://forums.asp.net/1146.aspx/1?MVC" title="ASP.NET MVC 
                    Forum">our forums</a>.
            </p>
        </div>
    </section>
}

Notice that sections in views can contain both static and dynamic content. Using sections enables you to organize the content in a view file without regard to the order in which it will be finally rendered. Sections also allow a layout file to specify multiple locations for dynamic content from the view being rendered.

Strongly Typed Views

Suppose that you have a list of Product instances you want to display in a view. One means of doing this is to simply add the products to the view data dictionary and iterate them over the view.

For example, the code in your controller action might look like this:

public ActionResult List()
{
    var products = new List<Product>();
        for(int i = 0; i < 10; i++)
        {
            products.Add(new Product {ProductName = "Product " + i});
        }
    ViewBag.Products = products;
    return View();
}

In your view, you can then iterate and display the products like so using Razor syntax:

<ul>
@foreach (Product p in ViewBag.Products){
    <li>@p.ProductName</li>
}
</ul>

The ViewBag contains dynamic properties and is not strongly typed, therefore a conversion is required in the foreach loop. The code would be cleaner if you could provide the view with the type for the model being sent in. This is where strongly typed views come in.

In the controller method, you can specify the model via an overload of the View method whereby you pass in the model:

public ActionResult List()
{
    var products = new List<Product>();
    for(int i = 0; i < 10; i++)
        {
            products.Add(new Product {ProductName = "Product " + i});
        }
    return View(products);
}

Behind the scenes, this sets the value of the ViewData.Model property to the value passed into the View method. The next step is to change the type of the view to inherit from WebViewPage<T>. The view really has no business having a code-behind file in the MVC model. In fact, by default, no code-behind files exist for views in ASP.NET MVC. If you want strongly typed views, just add the type to the @Model directive in the View like this:

@Model System.Collections.Generic.List<MvcBasic.Models.Product>

This is the preferred way to have strongly typed views. Now within the markup for the view, you can access the strongly typed ViewData.Model property, with full IntelliSense support:

<ul>
@foreach (Product p in Model){
    <li>@p.ProductName</li>
}
</ul>

Using HTML Helper Methods

One of the traits of the ASP.NET MVC Framework often touted is that it puts you in full control of your application, including the HTML markup. Many announce this as a benefit of the framework. After all, full control is good, right? But it’s really a characteristic of the framework that’s only good or bad depending on the circumstance.

At times you don’t want to have control over the markup. You would rather drop a control and have it figure out the markup because you don’t care how it looks. Other times, you want to have absolute control over the markup. Being in control is great, but it also means more responsibility. You are now responsible for outputting markup that would have otherwise been handled by a server control in the Web Forms world.

HTML helpers provide a middle ground. These are methods included with the framework that help with rendering markup for very common cases. In most cases, they handle common mistakes such as forgetting to encode attribute values and so on.

HtmlHelper Class and Extension Methods

The WebViewPage class has an HtmlHelper property named Html. When you look at the methods of HtmlHelper, you’ll notice they are rather sparse. This property is really an anchor point for attaching extension methods. When you import the System.Web.Mvc.Html namespace (imported by default in the default template), the Html property suddenly lights up with a bunch of helper methods.

In the screenshot in Figure 34-12, the extension methods are denoted by the gray down arrow.

FIGURE 34-12

image

One benefit of the HtmlHelper-style approach is that, because they are just regular extension methods, if you don’t like the helper methods included with the framework, you can remove this namespace and attach your own HTML helper extension methods. Likewise, it provides a convenient conventional place to add your own helper methods by simply writing extension methods of the HtmlHelper class.

All helpers share a few common patterns that are worth calling out now:

Views and their ViewEngines have a very specific, constrained purpose. They exist to take data passed to them from the controller, and they generate formatted output, usually HTML. Other than those simple responsibilities, or “concerns,” as the developer, you are empowered to achieve the goals of your view in any way that makes you happy.

SUMMARY

The ASP.NET Web Forms developer will need to get used to many differences when working with ASP.NET MVC versus Web Forms. In many ways, working with ASP.NET MVC will feel like “taking a step back 10 years” to classic ASP — especially when working with the UI and views.

For some, this is a welcome change and a breath of fresh air; for others, it just doesn’t work. It does take some getting used to, but in the end, the core ASP.NET functionality and the .NET Framework in general are there to support you.

Ultimately, the most important thing to remember is that ASP.NET Web Forms and ASP.NET MVC sit on top of ASP.NET proper. Think of it as ASP.NET > Web Forms and ASP.NET > ASP.NET MVC. There’s so much underneath both techniques that you can use either or both without fear. Many people find a hybrid model works for them, or they use a hybrid as they move from one model to the other. Pick the model that makes you feel most productive and run with it.