Pretty image
Microsoft’s Model-View-Controller web presentation framework is being called “Rails for .NET.”

Agile Microsoft?

No, it’s not an oxymoron. Things are changing at Microsoft, and if you haven’t taken a peek at what some of the guys in Redmond are up to recently, now is the right time. This March they released ASP.NET MVC (Model-View-Controller) 2.0—a web presentation framework that finally puts agile priorities at the center of a Microsoft framework—priorities like valuing testability and convention over configuration. On top of that it’s completely open source, so you can feel free to modify it to your heart’s content.

ASP.NET MVC—or, as some Rails developers call it, “Rails for .NET”—responds to the needs of agile web developers by making it easy to do test-driven development. For example, in traditional ASP.NET, you’d have to implement an MVP (Model-View-Presenter) design pattern to test-drive your pages. This isn’t such a big problem for experienced TDDers, but for beginners it was a real challenge to get started. Now in ASP.NET MVC, you can easily test-drive your controllers—right out of the box.?

Hello, World!

Let’s take a look at ASP.NET MVC at work. Accessing the URL http://localhost/hello will execute the controller HelloController.cs by convention:?

 HelloController.cs
 public ActionResult Index()
 {
  ViewData["message"] = "Hello World!";
  return View();
 }

The default URL creates a request that invokes the Index() action. All controllers have a special hash table called ViewData, which contains information to be displayed in the view. Here we simply pass the age-old “Hello World” using the key message. After we’ve completed this difficult processing, we specify which page we want to render by returning a view using the View() method. Based on another convention, this directs control to a View with the same name as the action—Index.aspx.?

 Views/Hello/Index.aspx
 <html>
  <body>
  <p> <%= ViewData["message"] %> </p>
  </body>
 </html>

This view renders the string as it was set in the controller. With ASP.NET MVC views you have full control of the markup. With these five lines of code we get a web page that renders these immortal lines:?

 Hello World!

The way that ASP.NET MVC matches easy-to-read URLs like http://localhost/hello to controller actions makes it much easier to program on the web—something it has in common with Rails. But I mentioned how simple it is to test ASP.NET MVC, so let’s take a look at the unit test using NUnit for the hello world example. We’ll be testing the HelloController’s Index() action:?

 HelloControllerTest.cs
 [Test]
 public void Should_Say_Hello_World()
 {
  var viewResult = new HelloController().Index();
  Assert.That("HelloWorld!", Is.EqualTo(viewResult.ViewData["message"));
 }

Here we create a new HomeController and capture the  Index() action’s ActionResult to run our assertion against. We then inspect ViewData’s message key for the expected “Hello World!” string, and the test will now pass. Testing in .NET just got a whole lot easier.?

More Rails-like Features

ASP.NET MVC also offers other features that are found in Rails. It has action filters, which can be applied to the controller as a whole or to an individual action:?

 [Authorize]
 public ActionResult SecureWorld()
 {
  ViewData["message"] = "Secure World!";
  return View();
 }

The [Authorize] filter stops someone from accessing the action unless they’re authenticated. Otherwise, it will prompt them to log in and then redirect them back once they’ve successfully logged in. ASP.NET MVC comes with loads of these action filters, including ones to help with output caching and error handling. You can also strike out on your own and create customized action filters.?

The 2.0 release of ASP.NET MVC now includes UI Scaffolding for prototyping. The scaffolding in ASP.NET MVC, just as in Rails, lets you skip creating a view. Instead, a generic view will take care of displaying pages for things like CRUD operations.?

ASP.NET MVC also comes with a decent routing engine that lets you create search engine–friendly URLs. The convention is to have URLs match http://localhost/{controller}/{action}/{id}, where {id} is the unique identifier of the model you’re looking at or editing. You can easily add your own customized routes to make URLs accommodate additional parameters; for example:?

 Global.asax.cs
 routes.MapRoute("DateRoute" ,
  "{controller}/{action}/{month}/{day}/{year}/{id}");

The first parameter of MapRoute() is the unique name of the route. The second parameter defines a series of variables that will be matched to the input URL. {controller} and {action} are matched to their respective controller and action. The other variables need to match to the parameters of the action. In this case, the action’s signature would need to be ActionForDateRoute(int month, int day, int year, int id).

Integrating with Other Frameworks

A big win for ASP.NET MVC is how simple it is to extend and integrate with other frameworks. It’s straightforward to tie in dependency injection frameworks, allowing you to make your web application more modular and testable. Also, ASP.NET MVC doesn’t come tied to any persistence framework—in fact it doesn’t come bundled with one at all. While some might find this a weakness, I think of it as a strength, because ASP.NET MVC lets you choose whichever persistence framework works for you and your team—NHibernate, iBatis, LINQ-to-SQL, or even MongoDB. Also, if you like a particular markup syntax like Haml or Velocity, you can replace ASP.NET MVC’s default engine with one of the many open source ones already available, like NHaml or NVelocity.?

When it comes to the markup, ASP.NET MVC hands you back full control. Views are rendered with simple HTML helpers instead of messy ASP.NET Web Form Controls. ASP.NET MVC 2.0 has some nice helpers like the EditorFor() method:?

 Html.EditorFor<Person>(person => person);

This helper method allows you to pass whole objects for editing. The magic of this method is it uses reflection to render the right HTML input controls based on the data type. Its close cousin DisplayFor() does the same, but for read-only screens.

Of course, Microsoft can’t do it all, and MvcContrib, an open source extension, helps fill in some of the gaps. First off, testing your controllers becomes even easier with MvcContrib. We can add another test to our earlier hello world example to check that the correct view is being rendered:?

 HelloControllerTest.cs
 [Test]
 public void Hello_Index_Renders_Index_View()
 {
  var viewResult =  new HelloController().Index();
  result.AssertViewRendered().ForView("Index");
 }

While this is a somewhat silly test, since we’re checking an ASP.NET MVC convention, it can prove useful as you begin modifying those conventions to render different views based on more complicated scenarios. MvcContrib makes it simple to create REST-style interfaces on top of pages you’re already serving in HTML. MVC Contrib makes it easier to test-drive your application by making tests shorter and more readable. Also, it has a few HTML helpers, like Grid, which makes it simpler to display tabular information.?

A bonus in the timing of ASP.NET MVC 2.0 is that Visual Studio 2010 and C# 4.0 have also just been released. C# 4.0 has some nice features like optional parameters that ASP.NET MVC can take advantage of. For example, if you’re creating a new Product model and you don’t want pass the id because there isn’t one yet, optional parameters allow you to leave the id blank without causing a validation error. Also, C# 4.0 now has a dynamic data type, which opens the door to more Rails-like conventions when generating and reusing views. Visual Studio 2010 itself is also slightly faster than its predecessor, an uncommon thing for Microsoft.?

All of this adds up to some big wins if you’re writing a .NET web application. If you’ve found yourself on a .NET project and were considering using ASP.NET Web Forms (aka traditional ASP.NET)—hold the phone, and consider ASP.NET MVC instead. It’s also one of the reasons the Pragmatic Bookshelf is giving it some attention in their library with Test-Drive ASP.NET MVC going to print this month.?

Jonathan McCracken is the Director of Services with ThoughtWorks Canada. He has developed .NET applications for a wide range of companies, including Microsoft. Jonathan has been developing software since 1994 and his .NET experience goes all the way back to .NET 1.0 in 2002. In addition to his professional activities, Jonathan enjoys speaking and blogging about diverse topics such as technology and the meaning of life.

Send the author your feedback or discuss the article in the magazine forum.