Wednesday, July 29, 2009

ASP.NET MVC in the Real World

MVC (the "model view control" pattern) isn't new, but it is new to ASP.NET. If you're like me, you may have been impressed by a demo, but you've probably thought "how does this work in the real world?" I hope to answer that question in this article.

All of the MVC demos that I have seen show how quick and easy it is to get going with ASP.NET MVC (from here on I'll just say "MVC"). They show a CSS'd up site with a navigation section, and a main content section. And in very little code, and very little time, they show how to retrieve data from your Model (the "Controller" does this), and how to display it in your page (the "View" does this). It look so simple, but there are some design questions that need to be addressed. We'll discuss them as we look into MVC.

MVC and Web Sites

The gist behind MVC is that a "View" is only responsible for displaying it's data, and nothing else. This means that if you had a View named 'UsedCars', it is only responsible for listing out used cars. There is no code for adding, deleting or updating the data in a View itself; there is no provision for displaying anything else other than "used car data".

So, MVC in a web application means that a View is not responsible for the navigation, header, footer or any other part of the site. With that being said, a few questions come to my mind. These are the items that this article will discuss as our 'real world' issues:

  1. How does MVC handle dynamic sections on a website that are not part of the current "View"? - For instance, on, if you're logged in, the navigation section changes to include member's only links. When you're looking at an article, the "related section" on the right of the site will show you blog posts that are similar to the content you are looking at.
  2. How does MVC work with validating a user input, and displaying a friendly message back to the user on a screen that he expects to see? The reason I mention this is because in MVC, an HTML form on one View will be posting to an Action (a different URL) that actually does the work. This is different than in the "Web Forms" world, where the page posts-back to itself. So if the user entered some invalid data, how do you send them back to the original View, fill out the form back to how they had it, and display the error message?

Aside from these "issues" above, we'll see how to achieve common functionality (that we all do every day in the "Web Forms" world). These include things like creating paging functionality (I won't explain this in the article, but it's in the source code at the end). So, to demonstrate how to build a "full featured" MVC site that needs to solve the above issues, we're going to build a 'used cars' website. There will be a list of cars for sale (that will be paginated), displaying results from a database. We'll also have a few static pages and a "contact us" form. I'll include the entire source code and project at the end of the article. Keep in mind, this article and demo was written for ASP.NET MVC Preview 3.

Building Our MVC Layout with MVC Master Pages

The MVC design pattern was not originally for web applications, so extending this architectural pattern to the web allows for some breathing room. This is where we will answer our first question: "how do we handle dynamic sections in our web site that isn't part of a particular View?" Well, the answer is the same for how we handle our entire web site (the layout and styling of it) outside of any one particular View - with Master Pages!

So the first thing we are going to do with our MVC "Used Cars" website, is to create our basic layout and styles, and make a 'stub' for our Views to live in (basically, a ContentPlaceHolder). Here's what our site will look like:

SingingEels_MVC Used Cars Site

Now, there are a few new tricks that we get with ASP.NET MVC (yes, I used the whole name here - for a purpose). The .NET team has provided a lot of powerful "helper functions" that snap right into the MVC framework. So, to make the navigation links above, I would normally have done this:

See full detail:

No comments: