The String or the Cat: A New .NET Framework Library

For years applications have been built that accept user input. Most user input starts out as a string. Strings are a universal representation of arbitrary data coming into a computer. However, most data does not remain as a string for very long. User input often ends up getting parsed or converted into another data type, such as an integer, Boolean value, or a date.

The concepts presented here are based on a thought experiment proposed by scientist Erwin Schrödinger. While an understanding of quantum physics will help to understand the new types and APIs, it is not required.

clip_image002

Mutation of data types leaves a software developer in an interesting situation if the data was not formatted properly and thus could not be mutated. For example, the string “abc123” cannot be parsed into an integer. Many frameworks deal with this situation by immediately reporting an error condition. That error could be an exception that was thrown or a Boolean value of false to indicate failure. Keeping track of these states can easily introduce uncertainty into an application.

StringOr<TOther>

The first class being introduced is the StringOr class. It is a generic class that encapsulates both the original string user input as well as the result of an attempted conversion operation.

namespace System.QuantumEntanglement {
    public class StringOr<TOther> {
        public StringOr(string stringValue, TOther otherValue);

        public string StringValue { get; }
        public TOther OtherValue { get; }
    }
}

The usage of the StringOr class is quite simple. An API that wishes to return data that might not have been parsed successfully can use the StringOr class to represent the dual state of its return value.

For example, a typical application might have a TextBox for entering an integer quantity:

clip_image004

If the consumer of this API wants the original value, they can use the StringValue property. Otherwise they can use the OtherValue property, which in this case is the successfully parsed integer.

The following diagram illustrates the dichotomy with a scenario familiar to many physicists:

clip_image006

SchrodingOr<TDead, TAlive>

The second class being introduced is the SchrodingOr class. It is similar to the StringOr, but with a distinct difference regarding its states. The SchrodingOr can represent any two data types: It is not restricted to one of the types being a string.

namespace System.QuantumEntanglement {
    public class SchrodingOr<TDead, TAlive> {
        public SchrodingOr(TDead dead, TAlive alive);

        public TAlive Alive { get; }
        public TDead Dead { get; }
    }
}

The usage of the SchrodingOr class is also similar to the StringOr. An API that wishes to return data that might be either of two data types, but never both data types at the same time, can return a SchrodingOr. The following code sample demonstrates a case where an API returns a value that can be either a bool or a DateTime.

clip_image008

Depending on the effects of quantum mechanics one of the two properties will return its value, while the other will throw a HeisenburgException. As before, the following diagram illustrates the dichotomy of the two possible values:

clip_image010

System.QuantumEntanglement Community Technology Preview

While the new .NET Framework library is still in relatively early stages of development, the team has decided to release a sneak preview to get feedback from our users. The link below contains the full source code for the new library, as well as unit tests that demonstrate proper usage of the libraries.

Please send all questions, comments, and suggestions to string.or@microsoft.com.

Many thanks,

The .NET Framework Quantum Mechanics Team

Posted by Eilon with 48 comment(s)
Filed under: , ,

ASP.NET MVC Release Candidate 2: I declare myself to be declarative!

Today is a happy day: we released ASP.NET MVC Release Candidate 2. We're getting ever-so-close to the final (RTM/RTW) release of ASP.NET MVC - it won't be long! We think we fixed all the highest priority issues so that everyone out there can build ASP.NET MVC applications without worrying that we're going to make some sudden changes that will break you. Please see Phil's blog post announcing the release.

In this blog post I'll introduce you to a feature that's not even part of the main ASP.NET MVC download. It's part of the separate MVC Futures download, which includes prototypes of features that we're thinking of including in a future version of ASP.NET MVC.

Introduction to the MVC Controls

Before we dig into the details, let's create a simple ASP.NET MVC application that uses the MVC Controls.

1. Download the MVC Futures binary from CodePlex.

2. Create a new MVC Web Application Project (if asked, feel free to not create a unit test project). Please note that you must have Release Candidate 2 installed. Each release of ASP.NET MVC has a matching release of MVC Futures. Mixing releases is not supported and usually doesn't work.

01-NewProject

3. Add a reference to the MVC Futures binary. Right-click on the References node in Solution Explorer and choose "Add Reference". Select the "Browse" tab and navigate to the location to which you saved Microsoft.Web.Mvc.dll, select the file, and select OK.

02-AddReference

03-References

4. Edit the web.config in the root of the application to include tag mappings to the MVC Futures namespace. This tells the ASP.NET parser where to locate the controls when you type in an <mvc:SomeControl> tag. There should already be some tag mappings listed - the only new tag mapping is the third one with "mvc" as the tag prefix.

<pages>
  <controls>
    <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add tagPrefix="mvc" namespace="Microsoft.Web.Mvc.Controls" assembly="Microsoft.Web.Mvc"/>
  </controls>
  ...

5. Start using the controls!

Let's start out with a simple scenario of updating some of the Account and Membership features of the default template to use the MVC Controls.

1. Open up the ~/Views/Account/LogOn.aspx view page. The markup there should include this text box:

<%= Html.TextBox("username") %>

2. Change the text box markup to use the MVC TextBox control:

<mvc:TextBox runat="server" Name="username" />

(Please note that it sometimes takes Visual Studio a few moments to recognize new tag mappings. If Intellisense doesn't show up, please just give it a minute.)

3. Run the application and click "Log On" at the top-right of the page.

04-Login

4. If all is well, the page should continue to have the exact same behavior as before. For example, if you immediately click "Log On" without typing in a username or password you should see the red highlight on the text box indicating an error.

05-errors

The idea of the MVC Controls is that they are largely modeled after the HtmlHelpers, and thus try to preserve all their behavior. This includes naming conventions, the markup they render, and usage of model state.

Advanced MVC Controls

For a more advanced scenario involving the MVC Controls I'd like to introduce the MVC Repeater control. In many ways it is very similar to the ASP.NET Repeater control, but the main difference is of course that the MVC Repeater has specific knowledge of ViewData.

1. In the application you already created, open ~/Controllers/HomeController.cs (Sorry, I don't have a VB sample!) and add this new action method and two classes:

public ActionResult ProductList() {
    List<Distributor> distributors = new List<Distributor> {
        new Distributor { DistributorID = 501, Name = "Joe's Shop", Location = "Redmond, WA" },
        new Distributor { DistributorID = 502, Name = "Jill's Shop", Location = "Los Angeles, CA" },
        new Distributor { DistributorID = 503, Name = "Jeremy's Shop", Location = "Boston, MA" },
    };

    List<Product> products = new List<Product> {
        new Product {
            ProductID = 1001, 
            Name = "LEGO Technic Off Roader", 
            UnitPrice = 119.99m,
            Distributors = new List<Distributor> { distributors[0], distributors[1] } },
        new Product { 
            ProductID = 1002, 
            Name = "Ferrari F430", 
            UnitPrice = 189000m, 
            Distributors = new List<Distributor> { distributors[0], distributors[1], distributors[2] } },
        new Product { 
            ProductID = 1003, 
            Name = "Five bedroom house in Bellevue, WA",
            UnitPrice = 540000m, 
            Distributors = new List<Distributor> { } },
        new Product { 
            ProductID = 1004, 
            Name = "Dell 2007FP LCD monitor", 
            UnitPrice = 439m,
            Distributors = new List<Distributor> { distributors[2] } },
    };

    ViewData["products"] = products;

    return View();
}

public class Product {
    public int ProductID { get; set; }
    public string Name { get; set; }
    public decimal UnitPrice { get; set; }
    public IEnumerable<Distributor> Distributors { get; set; }
}

public class Distributor {
    public int DistributorID { get; set; }
    public string Name { get; set; }
    public string Location { get; set; }
}

2. Right-click anywhere in the action method and choose Add View with the default options and select "Add":

06-addview

3. In the newly created view add this ASPX markup to the second content placeholder (right below the <h2> tag):

<mvc:Repeater runat="server" Name="products">
    <ItemTemplate>
        <b><mvc:Label runat="server" Name="Name" /></b>
        (PID#<mvc:Label runat="server" Name="ProductID" />)
        $<mvc:Label runat="server" Name="UnitPrice" /><br />
        Distributors:<br />
        <mvc:Repeater runat="server" Name="distributors">
            <ItemTemplate>
                - <b><mvc:Label runat="server" Name="Name" /></b>
                (DID#<mvc:Label runat="server" Name="DistributorID" />),
                located in <mvc:Label runat="server" Name="Location" /><br />
            </ItemTemplate>
            <EmptyDataTemplate>
                - Sorry, there are no distributors for this item<br />
            </EmptyDataTemplate>
        </mvc:Repeater>
        <br />
    </ItemTemplate>
</mvc:Repeater>
Please note that when you paste in the code Visual Studio might add an "ID" attribute to every control tag. You can delete all those attributes if you don't want them - but it's fine to leave them.

4. Run the page and you will get this nested databound rendering:

07-rendering

Advantages of MVC Controls Over Code Nuggets

If you've used the default WebFormViewEngine in ASP.NET MVC, you've certainly used this syntax somewhere in your views:

<%= Html.TextBox("FirstName") %>

What you might not know is that this syntax is called a "code nugget." While there is nothing inherently wrong with using code nuggets, one advantage of using MVC Controls is that they can provide a rich design-time experience. Many developers, even those who are familiar with the intricacies of HTML and CSS, like to get an accurate preview of what their page will look like without running the application.

If you've used ASP.NET Web Forms and controls such as the SqlDataSource and the GridView, you've seen how rich a design-time experience can be. Those controls include design time rendering previews, advanced template editing, and even wizards that walk you through the steps of configuring the controls.

The MVC Controls in the MVC Futures download are just a start: they certainly don't have the fit and finish that is seen in the SqlDataSource design-time experience. Like many rumors about ASP.NET MVC itself have stated, the beginnings of the MVC Controls were in fact written on an airplane!

Compare the areas in the following screen shot of the text box we created earlier to see the difference. The MVC Control has a proper preview, whereas the HTML Helper doesn't render at all.

08-DesignView

Disadvantages of MVC Controls

One of the biggest disadvantages of the MVC Controls is that there is a rather immediate limit to how you can use them. For example, take this relatively simple scenario with the regular TextBox HtmlHelper:

<%= Html.TextBox("FirstName", Model.FirstName.ToUpper()) %>

There is no simple way to do this with the MVC Controls because there is no place to add your own code to pre-process the values.

Another disadvantage is that the MVC Controls work only with the WebFormViewEngine. There are several other view engines out there that people have been using with ASP.NET MVC. Some of the popular third party view engines out there are Spark view engine, NHaml, and the view engines included in the MVC Contrib project, which are Brail, NVelocity, and XSLT.

For ASP.NET MVC 1.0 we decided on the MVC team to focus our efforts on features that have the widest possible benefit. In this case it meant writing helpers that worked with as many different view engines as possible.

As a slight bit of humor (though it's in fact serious), please refer to Eric Lippert's blog post How many Microsoft employees does it take to change a lightbulb?

The Future of the MVC Controls

As I mentioned in the top of the post, the MVC Controls are just a prototype. They are not complete; they are not set in stone; and there is no guarantee that they will ever be part of the main ASP.NET MVC download.

I already have a long list of improvements to make to the MVC Controls. I plan to add many more controls as well as more advanced features to existing controls.

If you have any feedback, comments, or suggestions on the MVC Controls feel free to post them here. We take all feedback seriously!

Some questions to think about:

  1. Do you see yourself using the MVC Controls?
    • If so, why?
    • If not, why not?
  2. What MVC Controls would you see yourself using the most? (Even ones that don't exist yet.)
Posted by Eilon with 35 comment(s)
Filed under: , ,

Opening an ASP.NET MVC project without having ASP.NET MVC installed: The project type is not supported by this installation

Projects built with ASP.NET MVC are what are known as "flavored projects." This means that instead of being an entirely new project type, ASP.NET MVC projects merely extend a different kind of existing project type. Specifically, the projects extend the Web Application Project type.

If you try to open a flavored project and don't have that particular flavored project system installed, you get to see this little slice of heaven:

Visual Studio error message: The project type is not supported by this installation

If you select "OK" then Visual Studio will continue, but the project will not be loaded:

Solution Explorer: Project unavailable

Well, then, what do you do to continue? All you have to do is edit the project file to remove the flavoring. Right-click on the project, and select "Edit MyProjectName.vbproj".

image

This will launch Visual Studio's project file editor (with Intellisense, in case you didn't know!). Towards the top you'll see a list of project types associated with this project, each one represented by a GUID.

  <ProjectTypeGuids>{603c0e0b-db56-11dc-be95-000d561079b0};{349c5851-65df-11da-9384-00065b846f21};{F184B08F-C81C-45F6-A57F-5ABD9991F28F}</ProjectTypeGuids>

The first GUID there (the one that starts with "603c") is the one used by ASP.NET MVC projects. Remove that GUID so you end up with:

  <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{F184B08F-C81C-45F6-A57F-5ABD9991F28F}</ProjectTypeGuids>

Save the project file and close it.

Now reload the project:

image

And you'll be able to work on the project once again. The only thing you won't be able to use is MVC project-specific features such as the "Add View" menu option.

I find myself doing this very often since my work machine is always in a bizarre state with respect to ASP.NET MVC and I often don't have the project type installed. This is much faster than trying to install MVC just so I can inspect a project.

Posted by Eilon with 8 comment(s)
Filed under: , ,

Optimizing your route collection for URL generation in ASP.NET MVC (and more!)

If you're used ASP.NET MVC or ASP.NET Dynamic Data, you've using ASP.NET's new Routing feature. ASP.NET Routing is the feature that lets you easily create so-called "pretty" URLs. Pretty URLs are the ones that look the way you want them to look (e.g. ~/products/beverages), not the way your web application platform forces them to look (e.g. ~/products.aspx?categoryname=beverages).

Many people, including myself, have blogged about the various features of Routing, but this post is about optimizing your routes to get the most out of them.

Here are some tricks to make URL generation more performant in your application:

1. Use named routes

Named routes are an optional feature of routing. The names are only used for URL generation; they are never used for matching incoming URLs. When you specify a name when generating a URL we will only try to match the one route with that name. This means that even if the named route you specified is the 100th route in the route table we'll jump straight to it and try to match. Otherwise we'd try every single route until we get to the 100th route. Here's an example of using named routes in an MVC application:

// Global.asax.cs

routes.MapRoute(
    "products-route",
    "products/{category}",
    new {
        controller = "products",
        action = "category",
    });

// Views/Home/Index.aspx

<%= Html.RouteLink("Show Beverages", "products-route", new { category = "beverages" }) %>

Note that if we hadn't specified the name route, we would probably get a different route to match. Also, because we used the named route, we didn't have to specify any extra route values such as the controller or action name.

2. Put your most common routes at the beginning of the route table

This will improve performance of both URL generation as well as processing incoming URLs. The Routing system works on a very simple rule: the first match wins. If the first match happens to be the 100th route in your route table, then that means it had to try 99 other routes and none of them matched. By putting your most common routes towards the top of the route collection, they will match sooner.

However, be careful: Changing the order of your routes can affect how they match. Only change the order of the routes if you're sure that it won't cause a conflict.

3. Don't use URL generation smile_sad

Some people like URL generation and some people don't. It's a bit tricky to master URL generation. It's nice to use URL generation if your URLs are very dynamic and you don't want to update your whole site when you make changes to your URL structure. But it can be a bit of a hassle when you have very few URLs to begin with and perhaps you don't care about exactly what they look like.

My favorite

My favorite option is #1, using named routes, primarily since it's super easy to use. It also makes URL generation more deterministic from the app developer's perspective (that's you!). By giving things names and then specifying exactly what you won't, it eliminates the guessing game of routes.

Posted by Eilon with 5 comment(s)
Filed under: , ,

Upcoming talks: Microsoft Tech Ed South Africa

In just a couple of weeks I'll be in Durban, South Africa presenting three sessions at Tech Ed. These are the sessions:

WEB304: Introduction to MVC Web Development

Day: Monday, 04 August 2008, 09:15 - 10:30
Location: Session Room 07
One of the benefits of using a MVC methodology is that it helps enforce a clean separation of concerns between the models, views, and controllers within an application. In the very near future, ASP.NET will include support for developing Web applications using an MVC-based architecture. The MVC pattern can also help enable red/green test-driven development (TDD) where you implement automated unit tests, which define and verify the requirements of new code, before you actually write the code itself. Join us for a dive into the new MVC Framework and learn how to leverage this new alternative in your own applications.

WEB306: What's new in Visual Studio 2008 for ASP.NET developers

Day: Monday, 04 August 2008, 16:15 - 17:15
Location: Session Room 08
Check out the latest and greatest with Microsoft Visual Studio 2008. From the CSS designer to JavaScript Intellisense, Visual Studio 2008 packs a powerful punch for building rich web applications. You’ll also see how to use LINQ data access and unit testing in a web application.

WEB307: A Tour of What’s New in ASP.NET 3.5

Day: Tuesday, 05 August 2008, 16:45 - 17:45
Location: Session Room 07
Discover how you can take advantage of the latest enhancements to ASP.NET and Visual Studio 2008 for building Web applications in your real-world applications. Enhance your web application with AJAX, Silverlight, and LINQ data access. You’ll also see how to use ASP.NET Dynamic Data to build a data entry site in a matter of minutes.

* Dates, times, and locations can change, so check your schedules!

I hope to see some of you there, so please stop by and say hi!

Posted by Eilon with no comments
Filed under: , , ,

HttpContext.IsCustomErrorEnabled - One of ASP.NET's hidden gems

Calling a property a gem might be a bit of an exaggeration, but this particular property is still rather valuable. In many situations a developer wants to enable a feature or part of a feature only for debugging and testing purposes. The IsCustomErrorEnabled property combines three values to tell you whether custom errors are enabled for a particular request. This isn't as simple as reading the web.config file to check the <customErrors> section. There's a bit more going on behind the scenes to truly determine whether custom errors are enabled.

The property looks at these three values:

  1. The web.config's <deployment> section's retail property. This is a useful property to set when deploying your application to a production server. This overrides any other settings for custom errors.
  2. The web.config's <customErrors> section's mode property. This setting indicates whether custom errors are enabled at all, and if so whether they are enabled only for remote requests.
  3. The HttpRequest object's IsLocal property. If custom errors are enabled only for remote requests, you need to know whether the request is from a remote computer.

The property is used in several places in ASP.NET to determine what type of error message to display to the user. When custom errors are enabled, the error messages tend to be terse so as not to disclose any secret information. When custom errors are disabled, more extensive information is displayed to the user including stack traces, type names, and file paths. One of the nice things about this property is that it works in all trust levels (well, at least Medium trust and higher - I'm not sure about Low and Minimal). It's also nice that you don't have to worry about reading from web.config manually.

Just recently in ASP.NET MVC we added a feature where we used this property to determine whether to show a generic error message such as "Your site is broken!" instead of full stack traces and debugging information. I hope this property ends up getting called by your ASP.NET applications and controls!

Posted by Eilon with 7 comment(s)
Filed under:

MVC: Unit testing controller actions that use TempData

Part of the "big deal" about ASP.NET MVC, and of course MVC in general, is that the code you write is more easily unit tested as compared to traditional ASP.NET WebForms. However, if you've used ASP.NET MVC and tried to write a unit test for a controller action that uses TempData, you probably got some exceptions about null references and other crazy stuff. I've seen a number of solutions out there that involve private reflection (which is kind of evil) so I figured I'd show how it can be done without that trickery. (Kudos, though, to those of you who figured out how to do such trickery!)

The underlying problem is that the TempDataDictionary object tries to read from session state when it's first used. At unit test time ASP.NET is not running so there isn't any session state. The solution is to provide a mock session state object to the TempDataDictionary. We're basically telling TempDataDictionary, "Hey you want some session state??? Here's your session state!!!"

The controller we're going to test has two actions. The first action adds a value to TempData and then does a redirect. The second action checks for the existence of the value in TempData and renders a view.

public class HomeController : Controller {
    public void Index() {
        // Save UserID into TempData and redirect to greeting page
        TempData["UserID"] = "user123";
        RedirectToAction("GreetUser");
    }

    public void GreetUser() {
        // Check that the UserID is present. If it's not
        // there, redirect to error page. If it is, show
        // the greet user view.
        if (!TempData.ContainsKey("UserID")) {
            RedirectToAction("ErrorPage");
            return;
        }
        ViewData["NewUserID"] = TempData["UserID"];
        RenderView("GreetUser");
    }
}

In order to write the unit tests I created a mock HttpContext with a mock session state object:

// HttpContext for TempData that uses a custom
// session object.
public class TestTempDataHttpContext : HttpContextBase {
    private TestTempDataHttpSessionState _sessionState =
        new TestTempDataHttpSessionState();

    public override HttpSessionStateBase Session {
        get {
            return _sessionState;
        }
    }
}

// HttpSessionState for TempData that uses a custom
// session object.
public class TestTempDataHttpSessionState : HttpSessionStateBase {
    // This string is "borrowed" from the ASP.NET MVC source code
    private string TempDataSessionStateKey = "__ControllerTempData";
    private object _tempDataObject;

    public override object this[string name] {
        get {
            Assert.AreEqual<string>(
                TempDataSessionStateKey,
                name,
                "Wrong session key used");
            return _tempDataObject;
        }
        set {
            Assert.AreEqual<string>(
                TempDataSessionStateKey,
                name,
                "Wrong session key used");
            _tempDataObject = value;
        }
    }
}
Each unit test that involves an action that uses TempData needs to create a custom TestTempDataHttpContext object and use that in a new instance of TempDataDictionary:
TestTempDataHttpContext tempDataHttpContext = new TestTempDataHttpContext();
homeController.TempData = new TempDataDictionary(tempDataHttpContext);

I wrote three unit tests for HomeController:

  1. Test that the Index action sets the right TempData and does a redirect
  2. Test that the GreetUser action redirects when the TempData is missing the value
  3. Test that the GreetUser action renders the GreetUser view with the right ViewData when the TempData has the right value

And here are the unit tests:

[TestClass]
public class HomeControllerTest {
    [TestMethod]
    public void IndexSavesUserIDToTempDataAndRedirects() {
        // Setup
        TestHomeController homeController = new TestHomeController();
        TestTempDataHttpContext tempDataHttpContext = new TestTempDataHttpContext();
        homeController.TempData = new TempDataDictionary(tempDataHttpContext);

        // Execute
        homeController.Index();

        // Verify
        Assert.IsTrue(homeController.RedirectValues.ContainsKey("action"));
        Assert.AreEqual("GreetUser", homeController.RedirectValues["action"]);

        Assert.IsTrue(homeController.TempData.ContainsKey("UserID"));
        Assert.AreEqual("user123", homeController.TempData["UserID"]);
    }

    [TestMethod]
    public void GreetUserWithNoUserIDRedirects() {
        // Setup
        TestHomeController homeController = new TestHomeController();
        TestTempDataHttpContext tempDataHttpContext = new TestTempDataHttpContext();
        homeController.TempData = new TempDataDictionary(tempDataHttpContext);

        // Execute
        homeController.GreetUser();

        // Verify
        Assert.IsTrue(homeController.RedirectValues.ContainsKey("action"));
        Assert.AreEqual("ErrorPage", homeController.RedirectValues["action"]);

        Assert.AreEqual(0, homeController.TempData.Count);
    }

    [TestMethod]
    public void GreetUserWithUserIDCopiesToViewDataAndRenders() {
        // Setup
        TestHomeController homeController = new TestHomeController();
        TestTempDataHttpContext tempDataHttpContext = new TestTempDataHttpContext();
        homeController.TempData = new TempDataDictionary(tempDataHttpContext);
        homeController.TempData["UserID"] = "TestUserID";

        // Execute
        homeController.GreetUser();

        // Verify
        Assert.AreEqual<string>("GreetUser", homeController.RenderViewName);
        Assert.AreEqual<string>(String.Empty, homeController.RenderMasterName);
        IDictionary<string, object> viewData =
            homeController.RenderViewData as IDictionary<string, object>;
        Assert.IsNotNull(viewData);
        Assert.IsTrue(viewData.ContainsKey("NewUserID"));
        Assert.AreEqual("TestUserID", viewData["NewUserID"]);
    }

    // Test-specific subclass for HomeController. This won't be
    // needed in the next release of ASP.NET MVC.
    private sealed class TestHomeController : HomeController {
        public RouteValueDictionary RedirectValues;
        public string RenderViewName;
        public string RenderMasterName;
        public object RenderViewData;

        protected override void RedirectToAction(RouteValueDictionary values) {
            RedirectValues = values;
        }

        protected override void RenderView(string viewName, string masterName,
            object viewData) {
            RenderViewName = viewName;
            RenderMasterName = masterName;
            RenderViewData = viewData;
        }
    }
}

Some notes about the code:

  • For the unit tests I also needed to create a test-specific subclass for HomeController called TestHomeController in order to capture the values of a call to RedirectToAction and RenderView. In the upcoming preview of ASP.NET MVC this class won't be needed due to some changes we've made.
  • Making TempData easier to unit test is on our list of improvements for a later preview of ASP.NET MVC. We realize it's a bit tricky right now, and we plan to make it much, much easier.
  • Instead of creating concrete classes for the custom HttpContext and session state objects you could use a mock object framework to dynamically generate those instances. I chose not to show that since it might detract from the purpose of the post. If you're already using a mock object framework such as RhinoMocks or Moq it should be easy to modify the code to use those frameworks.

I hope you find this sample useful when you're writing your unit tests. If you have any other pain points in ASP.NET MVC please post a comment so we'll know about it.

Posted by Eilon with 11 comment(s)
Filed under: , , ,

MVC: Locking the RouteCollection

Since the advent of multithreaded programming, the responsibility of locking collections has always been a contentious issue. Who should lock the collection? When should it be locked? And for how long? The ASP.NET Routing feature that is used by the MVC framework involves a thread-safe collection that contains the list of the application's route definitions. Will your routes be safe? Continue reading to find out (on this amazing journey (into the depths of the RouteCollection)). How's that for a cheesy intro?

The problem

When the first preview of ASP.NET MVC was released last December, some people noted that the RouteCollection would lock itself during all read and write operations, which could cause major performance and scaling issues. Well, sort of. The RouteCollection class derives from the generic Collection<T>, which has a few virtual methods it calls when write operations are performed. Each of those methods was overridden in RouteCollection to take a full lock. The route operations of matching routes to incoming requests and generating URLs also took the same full lock. There were two problems (at least) with this approach:

  1. Taking a full lock means that if anyone is using the collection, any other operation on that collection would have to wait for the first one to be done. Even if two requests just wanted to enumerate the collection (a read-only operation), one request would have to wait. This wasn't a logic bug since it wouldn't prevent anything from working; It was just a performance issue. There was in fact a comment in the code along the lines of "TODO: Shouldn't we use a ReaderWriterLockSlim here or something?"
  2. We weren't locking enough! If someone directly enumerated the collection by calling GetEnumerator (which happens implicitly when you use C# or VB's foreach statement), no lock would be taken. Unfortunately, Collection<T> does not offer any virtual methods that get called during read operations.

The solution (or so we thought)

We knew we had to do something for ASP.NET MVC Preview 2: We had performance problems as well as bugs in our collection locking. The first thing we did was change the lock to be a multiple-reader, single-writer lock with the ReaderWriterLockSlim class. Using this class in the RouteCollection allows any number of readers to read the collection at the same time but will fully lock the collection when a write is performed and allow only that one writer to make changes until the write lock is released.

Before we knew it, though, a bug was found while running some performance tests. When there is contention for the lock, the ReaderWriterLockSlim needs to get the processor count of the server. For some reason or another, getting that count is not permitted in Medium Trust web applications and thus throws a SecurityException. We didn't notice this while we were writing unit tests and web tests because all those tests are single threaded: there is never any contention for the lock.

The final solution

We opened a work item for the owners of Environment.ProcessorCount to remove the security requirement since knowing the processor count of the host is hardly top secret information. However, since the ASP.NET Routing feature works on .NET Framework 3.5, bug fixes in a future version wouldn't be sufficient. Thus we changed RouteCollection to use ReaderWriterLock, which is slightly less performant, but works in all web trust levels.

So we solved the problem of when the collection gets locked and for how long. But what about the very first issue I mentioned: Who should lock the collection?

Our first solution was to do all the locking internally. Any time you called any member on the collection we'd take the appropriate lock. This meant implementing IList<T> directly since neither Collection<T> nor List<T> provide the necessary hooks. This proved to be a lot of work (but not for me, since I didn't have to write it smile_regular) and left some big holes of functionality. For example, how could someone atomically search for a route in the collection, remove it, and add a new route?

The solution to that problem was to expose the lock semantics publicly, though without exposing the lock object itself. Thus the RouteCollection has two new methods: GetReadLock and GetWriteLock. Developers can call those methods to get the lock that the RouteCollection uses internally and cooperate with its locking mechanism. However, you only need to get the lock if you're directly accessing the collection. If you're going through any of the typical "end-user" API calls, we do the locking automatically. For example, calling Html.ActionLink, Url.Action, and RouteCollection.GetVirtualPath will automatically lock the collection.

In case you're wondering, there were several reasons we chose not to expose the lock object itself:

  1. We wanted to be able to change the lock implementation at any time. This would let us switch back to ReaderWriterLockSlim at some point in the future.
  2. It seemed simpler for users to have a limited API surface. Less is More.
  3. It encourages users to use the dispose pattern with the lock they get, thus ensuring that the lock is released even if an exception is thrown and not caught while the lock is being held.

Why you should care

There are two takeaways I hope I provided in this article:

The first takeaway is that if you're implementing a thread-safe collection you have some food for thought. I hope the food was easy to digest!

The second takeaway is that if you're directly accessing the RouteCollection in an ASP.NET application, make sure you take the lock first! Taking the lock is easy with the dispose pattern:

RouteCollection routes = RouteTable.Routes;
using (routes.GetReadLock()) {
    foreach (RouteBase route in routes) {
        // Do something interesting here and be guaranteed that no one is modifying the collection
    }
}
// The lock is now released since we exited the "using" statement.

Who would have thought that a simple collection of routes would turn out to be so much fun? Did you ever start coding something that seemed simple and ended up writing a long blog post about it?

Posted by Eilon with 11 comment(s)
Filed under: , ,

MVC: I'll need a little bit more context on that

ScottGu recently announced the plans for the next public preview of the ASP.NET MVC framework. For those who don't feel like clicking on links to other peoples' blog posts Scott's blog says it's being released at MIX08, which is only a couple of weeks away. And yes, I will be there!

My previous blog post talked in general about the design philosophy of the ASP.NET MVC framework. Today I'd like to talk specifically about context objects, how we use them in ASP.NET MVC, and why we like them. In the coming release of MVC there will be about seven context types throughout ASP.NET, URL routing, and MVC.

What is a Context Object?

A context object is exactly that: An object that gives another object some information about what's going on. Information such as what is the current request, what task is being performed, and sometimes even the ability the change what's going on.

Context in MVC

One context object many ASP.NET developers are already familiar with is HttpContext. HttpContext gives you everything you need to know about the current request, who's making that request, and how to respond to the request. Of course, in MVC we decoupled the MVC framework from the test-unfriendly HttpContext by introducing an IHttpContext interface (which, by the way, is now an HttpContextBase abstract base class for future-proofing purposes).

However, as the request gets processed we create and gather up additional information about what's going on. The first context we add is at the routing level: the RequestContext type. It adds just one piece of information, the RouteData, as well as including all previous context information, which is just the HttpContextBase.

Once a route is selected and we determine that it is an MVC request, we're off to the controller. This is where the ControllerContext comes in to the picture. ControllerContext derives from RequestContext and adds the controller instance to it. We then have a ViewContext type, which adds information about the selected view, its view data, and some other items.

One of the new features in the upcoming preview is action filters, and sure enough there are three filter context types as well.

Since the context types derive from one another (except for HttpContextBase), you can often pass in whatever context type you have to call some method from a lower level. For example, in the view you get a ViewContext that you can pass into the URL routing methods.

What Can Context Do For You?

By passing around context objects we have no need for global state. Global state would be something like a static property or field that everyone can get to. One of the typical requirements of a unit test is that during their execution they do not consume global state nor do they alter global state. We give you the context object and it has everything you need to know. How do I know that it's everything you need to know? Quite simply, the context object is all we know anyway, so we're just letting you know about it as well.

Since your code now makes its decisions and operates on a passed-in context object, your unit tests can produce fake contexts and pass those in to your methods to see how the code reacts to them. You no longer have to worry about your code depending on global state since there simply isn't any (at least none that we create).

Have I given you enough context on what we're doing?

Posted by Eilon with 5 comment(s)
Filed under: , , ,

ASP.NET MVC Design Philosophy

This week the first preview of the ASP.NET MVC framework was released to the web as part of the ASP.NET 3.5 Extensions CTP Preview. It's been a few months since we started coding and as the lead developer on the project I'd like to share my thoughts on the design of this framework. This isn't a post about why MVC is great. Instead, it's a post about what we did to make MVC happen in ASP.NET.

The MVC Snake

One of the design strategies we used in the MVC framework is to have a clear sequence of stages in each request. The stages are reminiscent of the ASP.NET Page and Controls lifecycle, but much, much simpler. Here's a rough diagram of the stages from the slides that Scott Hanselman and I showed at the DevConnections conference.

MVC_Snake

When a request comes in, it goes through the stages as shown in the diagram. There are a few more stages than are shown, but they were removed so the "MVC Snake" diagram wouldn't get too cluttered. An important aspect of this diagram is that each stage only depends on the previous stages. A given stage typically doesn't make any assumptions about what follows it.

For example, the IController interface doesn't assume anything about views or any particular view factory implementation. However, views can assume that an IController was involved in their creation. The implication of this is that you can stray from our path at almost any stage and still end up with success. Want to implement a controller in some new, unique way? Go ahead!

URL Routing - Not part of MVC

Part of the MVC framework is a new URL routing mechanism. This allows you to create "friendly" and "pretty" URLs such as "http://example.org/store/categories" that map to arbitrary controller actions. There's no reason that the URL should specify a physical resource. The visitors to your site surely don't care how you architected your web site or web application.

If you've downloaded the CTP you'll notice that the URL Routing types sit in the System.Web.Mvc namespace. However, the URL Routing types aren't even a part of the MVC framework. URL Routing works with arbitrary IHttpHandlers so it can serve up arbitrary requests - not just MVC requests. For the current CTP because we just couldn't find a better place so we left the types in that namespace.

New Language Features

We're technology addicts, and it shows. The latest version of the C# language includes several new features and we decided to take advantage of them in the MVC framework. We used anonymous types in place of dictionaries, extension methods for helper methods, and lambda expressions as strongly-typed URLs.

But - and there is almost always a "but" - we went a bit too far in the current CTP. We've heard from many of you already that using anonymous types as dictionaries is cool, but hard to understand. And it doesn't have Intellisense. For the next release we're still planning on keeping those features, but we want to add more familiar ways of doing the same things. For example, we're going to let you use dictionaries when we want dictionaries. It seems obvious now. :)

Extensible, Extensible, Extensible

To build a framework that was extensible, testable, and functional, we had many important design decisions to make.

Perhaps most importantly, we went with an interface-based design. We didn't use interfaces for everything, but we did for most things. Typically we used interfaces for all behavioral types, and classes for data objects. The reason is that behavior needs to be mocked, whereas data is just data. Your data objects would probably look exactly the same as our implementations anyway, so we didn't want to bother with interfaces. There are always exceptions, and in the first CTP we may have made some decisions that don't agree with this, but we already have plans to address these for the next CTP.

Traditionally in ASP.NET, a lot of extensibility is enabled by inheriting from our classes and overriding virtual members. While it is a valid approach, it requires that you inherit from our components even if you just want to tweak one small behavior. In the MVC framework we still have many instances of that, but we also allow you to extend our functionality using composition. For example, to modify the behavior of a controller you can either derive from it or you can use a filter to externally change its behavior. The same exception I mentioned in the previous paragraph applies here.

Composition often yields better separation of concerns and allows you to more easily build cross-cutting concerns. For example, if you want to apply the same "behavior modification" to several different controller classes, you don't have to override the same virtual members in each one. You can just apply an external cross-cutting concern to the controllers you want. Thus, composition is more easily used with dependency injection frameworks.

What Does All This Lead To?

Well, what does this all lead to? Several things, of course. For example, each of the components in the MVC framework is fairly small and self contained, with single responsibilities. This means that due to their small size you have building blocks that are easier to understand. It also means that you can replace or even alter the building blocks if they don't suit your needs.

Having great amounts of extensibility means you can write unit tests for your application more easily. I remember the days when I didn't write unit tests. Dark days, they were.

But who is going to use the MVC framework? Not everyone wants to write unit tests or alter so-called building blocks. Being able to drag & drop a GridView and a SqlDataSource onto a WebForm is a valid scenario and it will continue to work. We're trying to create a better fit for ASP.NET for certain types of individuals.

Anecdotes and Miscellany

And to end with some fun facts and stories, the following is all true.

  • For the released CTP, the unit test code coverage numbers were about 93%, far more than any other major feature area. This does not include the code coverage by our QA guys, which I'm sure would bring the number up to at least 99%.
  • We had about 250 unit tests for MVC, and the ratio of unit test code to product code is about 1.9 to 1 (in terms of the size of the code files, not lines of code; I'm too lazy to do the latter!).
  • It feels good to unit test! I can't go back to the old ways.
  • Some of the code is written using true TDD, but some is code-first, but it is then quickly followed up with unit tests (prior to checking in).
  • Several weeks ago we invited several customers for a top secret sneak preview of the CTP, including the MVC framework. Jeffrey Palermo was attending and trying to build some samples with the MVC framework. Sure enough, he encountered some bugs in the product. The bits were still hot off the press and some of the bits hadn't quite settled in the right place. Millions of ones and zeros - what are the odds they'd all line up correctly on the first try? Anyway, I sat with Jeffrey for about half an hour and we managed to use many of the extensibility points in the framework to work around the bugs he encountered. The whole time I kept thinking (and probably saying aloud) that I was so glad I made those methods public or virtual or whatever! Without them Jeffrey would have been stuck.
  • The moral of the story: If you aren't sure, make it public. If we have to use a method, chances are, someone else does too.

MVC Resources

Downloads:

Documentation and discussion forums:

Blog posts and videos by team members:

Posted by Eilon with 39 comment(s)
Filed under: , , ,
More Posts Next page »