ASP.NET MVC 3–What’s in it for you?
ASP.NET MVC 3 was released on January 13. You've probably seen the blog posts, especially:
- Phil Haack: ASP.NET MVC 3 Released
- Scott Hanselman: ASP.NET MVC3, WebMatrix, NuGet, IIS Express and Orchard released - The Microsoft January Web Release in Context
- Scott Guthrie: Announcing release of ASP.NET MVC 3, IIS Express, SQL CE 4, Web Farm Framework, Orchard, WebMatrix
You can get the feature summary, including an in-depth MVC 3 feature overview, at http://asp.net/mvc/mvc3. I coordinate that page and make sure the MVC 3 Release Notes get htmlized, so we could talk bullet points all day long. But let's try to boil it down to the proverbial elevator pitch. Why should you care that ASP.NET MVC 3 is release enough to start using it today? Why would your pointy-haired boss consider using MVC 3 on your next project?
Beyond Bullet Points - The Elevator Pitch
I've been doing training events and writing tutorial/sample code using ASP.NET MVC 3 for a while, and I've been thinking about the overall "feel" of ASP.NET MVC 3 from the viewpoint of a developer. I just finished a full rewrite of the MVC Music Store tutorial for MVC 3 (using Razor views layouts, NuGet, and Entity Framework Code-First), and that provided a great opportunity to compare the overall development experience in building the exact same application twice in two different versions of ASP.NET MVC.
Note: I didn't write the tutorial update with this in mind, so I'd like to think this comparison, despite other possible faults, is at least an honest one.
Looking back at the ASP.NET MVC releases, I'd summarize them as follows:
- ASP.NET MVC 1 - The best of ASP.NET and .NET development with the benefits of the maintainable, testable, extensible MVC pattern
- ASP.NET MVC 2 - Templating and attribute-based validation which make it easier to display information and forms
- ASP.NET MVC 3 - Razor makes views simpler, NuGet makes takes the risk and pain out of open source, and advanced features make extensibility painless
Of course, all three releases have included a lot of advanced features and aren’t really focused on a single theme. We tried to pin Phil down to a theme for the ASP.NET MVC 2 release when we interviewed him on Herding Code, and he refused to go along with it. MVC releases have an agile development feel – each release includes the top features that will bring the most value to the ASP.NET MVC community community, so as in any agile project I’ve worked on, the theme for each release is “the best software we could possibly give you today.”
So I focused instead on how I’d summarize each release to someone who was interested, but not convinced. I really tried to distill the feel of MVC 3 down to a single phrase, and I just couldn't do it. Let’s look at the three joys of ASP.NET MVC 3 from a developer perspective.
Razor makes views feel more like HTML, less like code
When you’re writing View code, your focus is on generating HTML to represent your model. You’ve got two contexts to consider – static HTML and code that writes HTML based on your model. Switching between those contexts is distracting. Razor is a new view engine for ASP.NET MVC that makes your view code feel more like a view and less like code. Here's the Store/Browse view in ASP.NET MVC 3 with Razor:
@model MvcMusicStore.Models.Genre @{ ViewBag.Title = "Browse Albums"; } <div class="genre"> <h3><em>@Model.Name</em> Albums</h3> <ul id="album-list"> @foreach (var album in Model.Albums) { <li> <a href="@Url.Action("Details", new { id = album.AlbumId })"> <img alt="@album.Title" src="@album.AlbumArtUrl" /> <span>@album.Title</span> </a> </li> } </ul> </div>
Here's that same view in ASP.NET MVC 2 using the Web Forms View Engine:
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcMusicStore.ViewModels.StoreBrowseViewModel>" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> Browse Albums </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <div class="genre"> <h3><em><%: Model.Genre.Name %></em> Albums</h3> <ul id="album-list"> <% foreach (var album in Model.Albums) { %> <li> <a href="<%: Url.Action("Details", new { id = album.AlbumId }) %>"> <img alt="<%: album.Title %>" src="<%: album.AlbumArtUrl %>" /> <span><%: album.Title %></span> </a> </li> <% } %> </ul> </div> </asp:Content>
This is a relatively simple view, so there's not a huge amount of code in either case. I this specific case, we dropped about 6 lines of code in a 27 line view. Lines of code can be abused as a metric, but that's nearly a 25% savings on a short view. I've been noticing that's largely because:
- Context switches between code and markup are lightweight, and
- The Razor layout system requires a lot less of the view code - there's no need to use <asp:Content> blocks to indicate where content should go
Line count doesn't tell the full story, though. For me, as I was writing the code, I noticed that there was a lot less friction in the context switching within the lines of code as well. It was time to look at the character count changes, and I decided to do that using PowerShell.
I'm still working on my relationship with PowerShell. I like what it does, but there's something funky about the syntax that makes it a little hard for me to adsorb. After a few quick searches, I had this short command which gives me the character count for all files in a directory:
dir -Include *.* -Recurse | get-content | measure-object -character -word -line -ignoreWhiteSpace
That gives me the following results:
Lines | Words | Characters | |
MVC 2 | 593 | 1718 | 17496 |
MVC 3 | 582 | 1370 | 13897 |
Even that doesn't tell the full story - a 20% character count reduction is nice, but that doesn't get close to the qualitative feel in moving from the Web Forms view engine to Razor. It feels like I'm writing half the code in each view, because it flows so much more smoothly.
Looking at the Metrics
Next, I looked at how this affected the overall code count and complexity using the Code Metrics feature in Visual Studio (available in Premium and Ultimate editions). This is a great way to get a rollup view of how complex and maintainable your code is - and you can drill in to find problem areas, export the results, etc.
Here's how things looked with the MVC 2 release of MVC Music Store:
And in MVC 3
The obvious change here is in the Models folder, which shouldn't be surprising since we moved to Entity Framework Code-First with this new release.
But let's think about that a bit more. Even if EF Code First had been further along when I was doing the MVC 2 version, I'm not sure I would have used it. I'm hugely excited about the technology, but there are a few too many moving parts to explain in getting EF Code set up and integrated in a project to dig into that, even in an intermediate level post.
So why did I use EF Code First in this beginner level tutorial? Because I could install it using NuGet.
NuGet makes annoying things embarrassingly easy
I've been a fan of using additional libraries in my .NET projects for a long, long time - sometimes prerelease bits, sometimes open source libraries. It's something that's been a big effort at times - worthwhile, but still an effort. Here's the general flow:
- Search around for something that sounds like it might work
- Find the website and look around for a download
- Figure out how to install and configure the library
- See if it worked
- Repeat steps 2-4 until it works or you give up
None of that qualifies as rocket science, but it's just difficult enough that:
- Deciding to use a library requires weighing the benefit that the library brings against the uncertainty and pain of getting it to work
- You have to consider if other people who will be using the code will be able to get it to work and fix it when they run into trouble
NuGet flips that on its head by making it extremely easy to both install and uninstall software libraries. So the decision to use EF Code First was a lot simpler, because folks who are following the tutorial can add EF Code First to their project by following some very simple steps, and without ever leaving Visual Studio.
MVC 3 makes hard things embarrassingly easy
We don't leverage the new service location features in the MVC 3 for dependency injection in the MVC Music Store tutorial, but it's not much of a stretch. It's simple enough that it's included in the first day of the Web Camps content for for MVC 3, and attendees at Web Camps have no trouble with the DI lab on Day 2 of Web Camps. That's a big deal, as Dependency Injection has generally been just difficult enough to implement that only advanced developers working with mature teams on long-running projects ever made use of it. When I was helping to write the MVC 3 content for the current wave of Web Camps, I was really happy with how smoothly and simply Dependency Injection fit into code demonstrations we'd be showing to beginning and intermediate level developers.
I think that's a remarkable achievement - a framework that makes hard things easy, and easy things even easier.