asp.net, asp.net mvc, code comments edit

It’s that time of year again when the sakura (cherry blossoms) bloom and allergies kick into high gear. When the drive home is no longer shrouded in darkness and when the ASP.NET team releases Software!

sakuraEarlier today we released the RTM of ASP.NET MVC 2 for Visual Studio 2008 SP1. See Scott Guthrie’s announcement about it. For download and install options, visit the ASP.NET MVC Download Page.

Here are a few helpful resources to help you learn more about this release.

My team and I are excited about this release as it adds a lot of great new functionality for those building web applications with ASP.NET MVC. As with ASP.NET MVC 1.0, the community (yes, that’s you people!) were heavily involved in the making of this product via multiple preview releases. So thank you!

What about Visual Studio 2010?

Keep in mind that this release includes project templates for Visual Studio 2008. Visual Studio 2010 RTM will be released fairly soon and that will contain project templates and runtime for ASP.NET MVC 2 RTM in the box.

If you have Visual Studio 2010 RC installed, you’ll find that the installer for ASP.NET MVC 2 RTM is blocked just as it was for ASP.NET MVC 2 RC 2.

To remedy the situation, you just need to uninstall the ASP.NET MVC 2 runtime first, and then run the ASP.NET MVC 2 RTM installer.

I’ve outlined the steps in more detail in a previous blog post describing how to install MVC 2 RC 2 on a machine with Visual Studio 2010 RC. These same steps apply, but switch “RC 2” with “RTM” of course. :)

Again, sorry for the inconvenience. Once Visual Studio 2010 RTM ships, this will all just work.

Source Code! Source Code! Source Code!

As always, the source code, along with our Futures library, is available on CodePlex. For those looking for the Ms-PL package, we will be releasing the source code for System.Web.Mvc.dll very soon.

What’s Next?

I’m glad you asked. While we’ve been very busy getting this release ready, preparing for Mix 10, closing down on ASP.NET 4 and Visual Studio 2010, we’ve still found time to start preliminary planning for ASP.NET MVC 3.

It’s very early and nothing is written in stone yet (of course not, it’d be written in C#), but you can read our rough roadmap that outlines some of the ideas we have for the next version.

asp.net, asp.net mvc, code, personal comments edit

If there’s one impression that Austin left on me, besides the one that Rudy’s “extra moist” barbecue left on my gut, is that it’s a developer friendly town.

This past week I spent three days in Austin meeting with all sorts of developers and had many great conversations about technology. I met with companies and people with a passion for technology that couldn’t be suppressed.

Photo from
http://www.treehugger.com/

Meeting with Dell

The reason I was in town was to give a couple of presentations at a mini-conference for Dell employees. On my first day in Austin, the day before the conference, I met with a team working on the next generation of Dell.com. The site is undergoing a large scale rearchitecture based heavily on ASP.NET MVC 2.

I fielded some questions on ASP.NET MVC and its future and was given a presentation on their new architecture. It was clear that they spent a lot of time with ASP.NET MVC as I even received an obscure question asking why Routing uses the ReaderWriterLock rather than ReaderWriterLockSlim? (The answer, in case you’re interested in that sort of trivia, is that ReaderWriterLockSlim didn’t work in medium trust in ASP.NET 3.5 SP1, but that’s been fixed in ASP.NET 4 and Routing now uses that).

I should mention that Dell is hiring in a big way. Many of these positions will involve a lot of ASP.NET MVC development.

We’re looking for senior level folks that have extensive e-commerce experience, are experts in Microsoft technologies (MVC knowledge a big plus), and have worked on some very large sites.  We’re looking for architects, dev leads, business analysts and project managers.  We have positions open in Round Rock, Tx (just north of Austin) as well as Bangalore, India.  Visit www.dell.com and click on Careers at the bottom of the page.

Austin .NET User group

After meeting with Dell, I was whisked over to the Austin .NET User Group meeting (ADNUG) where I gave a presentation on what’s new in ASP.NET MVC 2. The room was packed and the attendees were engaged with the talk. They had to be as my demos were failing left and right and I needed the help from the audience to catch my mistakes several times. Thank you ADNUG!

Afterwards I went with several user group attendees and was treated to Rudy’s Country Store and Bar-B-Q which is a gas station, convenience store, and Bar-B-Q shack. I would soon learn that Austin is full of these multi-function restaurants. I ordered the “Extra Moist” and some cream corn which was oh so delicious!

Back at Dell

After Rudy’s I spent the evening working on digestion and my demos. The next morning I spent the day at a small conference center presenting two talks to Dell employees, the intro talk I mentioned earlier and an ASP.NET MVC 2 Tips and Tricks talk.

This time, both talks went much more smoothly and I now feel much more prepared for the upcoming Mix 10.

After my talks at Dell, Jonathan Carter and I headed over to the WhipIn to meet up with the quiet and always subdued Scott Bellware. The WhipIn is a convenience store, bar, and Indian food restaurant with a slogan of “Namaste Y’all!”” Remember that thing I said earlier about multi-function restaurants?

Visiting Headspring

My last day in Austin was spent visiting two companies full of very talented software developers doing extensive work with ASP.NET and ASP.NET MVC.

I spent the morning at Headspring which employs many well known members of the .NET community as they’re involved in blogging, C4MVC, MvcContrib, etc. Jeffrey Palermo, Eric Hexter, Jimmy Bogard, Matt Hinze, amongst others (sorry, I don’t know your URLs).

One of the first things I noticed when walking into their office was a big screen TV displaying the statuses of several different builds from their Continuous Integration (CI) server. I watched as a Watin run provided a “ghost in the machine” demonstration of their automated functional test suite.

They walked me through their current project showing how they’re using and customizing ASP.NET MVC and we discussed potential improvements to ASP.NET MVC. One thing they’ve done as they’ve gained experience building ASP.NET MVC applications is to embed many of the lessons they’ve learned into the open source CodeCampServer project.

It may be overkill for what it is, a code camp management website, but the point of the site is to demonstrate practices they use on much larger projects.

Visiting Dovetail

After meeting with Headspring, I headed over to meet the DoveTail guys for lunch. Unfortunately, we didn’t go to Torchy’s since we were short on time and didn’t want to wait. Not only that, I had a sangria margarita for lunch and was carded. Those Austin-ites are sticklers for checking IDs.

We spent lunch discussing a wide range of topics on the .NET community, technology, etc. After lunch we headed over to their office where once again I saw a monitor with the status of their continuous integration server displayed on a monitor. Seems all these Austin developers see the value in continuous integration.

We settled in and Jeremy Miller, Chad Myers, Joshua Flanagan, et all walked me through what they’ve done with FubuMVC. I really liked several of the ideas they’ve put in their framework, some of which may show up in ASP.NET MVC in the future as is typical of technical cross-pollination.

Heading Home

After the meeting at Dovetail, I took a cab to the Austin airport to catch a flight home. Looking back, I wished I had been able to spend more time in Austin. Known as the live music capital of the world, I didn’t get a chance to hit the heart of the city.

Even so, I really enjoyed my visit there. The weather was nice and the local tech community seemed to be quite involved and vibrant. It’s definitely a place I want to visit again.

asp.net, asp.net mvc, code comments edit

During the MVP summit, an attendee asked me for some help with a common scenario common among those building content management systems. He wanted his site to use human friendly URLs.

  http://example.com/pages/a-page-about-nothing/

instead of

  http://example.com/pages/123/

Notice how the first URL is descriptive whereas the second is not. The first URL contains a URL “slug” while the second one contains the ID for the content, typically associated with the ID in the database.

This is easy enough to set up with routing, but there’s a slight twist. He still wanted the action method which would respond to the first URL to have the integer integer ID as the parameter, not the slug. Let’s look at one possible approach to solving this.

Here’s an example of what the route might look like:

routes.MapRoute(
  "Slug", // Route name
  "pages/{slug}", // URL with parameters
  new { controller = "Home", action = "Content" } // Parameter defaults
);

Notice that the route URL contains one parameter for “slug” and no “id” parameter whatsoever. Here’s an example of the controller action that route should map to.

public ActionResult Content(int id)
{
  // Note the argument is an id, not slug
  return View();
}

Note that the action method does not accept a parameter named “slug” but instead expects an integer “id” parameter.

Fortunately, there’s an easy way to do this. Action filters, classes which derive from ActionFilterAttribute, allow hooking into the point in time after the parameters of action method have been bound, but just before the action method has been invoked. This gives us a fine opportunity to muck around with the parameters.

The following is an example of an action filter which converts a slug to an ID (you can imagine a real one would probably look it up in the database, not in a static dictionary like the sample does).

public class SlugToIdAttribute : ActionFilterAttribute
{
  static IDictionary<string, int> Slugs = new Dictionary<string, int>
  {
    {"this-is-a-slug", 100}, 
    {"another-slug", 101}, 
    {"and-another", 102}
  };

  public override void OnActionExecuting(ActionExecutingContext filterContext)
  {
    var slug = filterContext.RouteData.Values["slug"] as string;
    if(slug != null)
    {
      int id;
      Slugs.TryGetValue(slug, out id);
      filterContext.ActionParameters["id"] = id;
    }
    base.OnActionExecuting(filterContext);
  }
}

The filter overrides the OnActionExecuting method which is called just before the action method is called. The filter than grabs the slug from the route data, and looks up the corresponding id. Now all we need to do is make sure the id is passed into the action method.

Fortunately the filter context passed into this method allows us to peek into the parameters that will get passed into the action method via the ActionParameters property. Not only that, it allows us to change them!

In this case, I’m grabbing the slug from the route data, and looking up the associated id, and adding a parameter named “id” to the action parameters with the correct id value.

All I need to do now is apply this filter to the action method and when the action method is called, this id will be passed into the method.

This works whether the argument to the action method is a simple primitive type as in this example or whether it’s a complex type. I’ve included a sample project that demonstrates changing parameters to action methods via an action filter.

asp.net, asp.net mvc, code comments edit

If you have a model object with a property named Id, you may have run into an issue where your model state is invalid when binding to that model even though you don’t have an “Id” field in your form.

The following scenario should clear up what I mean. Suppose you have the following simple model with two properties.

public class Product {
    public int Id { get; set; }
    public string Name { get; set; }
}

And you are creating a view to create a new Product. In such a view, you obviously don’t want the user to specify the Id.

<% using (Html.BeginForm()) {%>

  <fieldset>
    <legend>Fields</legend>
        
        
    <div class="editor-label">
      <%= Html.LabelFor(model => model.Name) %>
    </div>
    <div class="editor-field">
      <%= Html.TextBoxFor(model => model.Name) %>
      <%= Html.ValidationMessageFor(model => model.Name) %>
    </div>
       
    <p>
      <input type="submit" value="Create" />
    </p>
  </fieldset>

<% } %>

However, when you post it to an action method like so:

[HttpPost]
public ActionResult Index(Product p)
{
    if (!ModelState.IsValid) {
        throw new InvalidOperationException("Modelstate not valid");
    }
    return View();
}

You’ll find that the model state is not valid. What gives!?

Well the issue here is that the Id property of Product is being set to an empty string. Why is that happening when there is no “Id” field in your form? The answer to that, my friend, is routing.

When you crack open a freshly created ASP.NET MVC 1.0 application, you’ll notice the following default route defined.

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = "" }
);

To refresh your memory, that’s a route with three URL parameters (controller, action, id), each with a default value (“home”, “index”, “”).

What this means is if you post a form to the URL /Home/Index, without specifying an “ID” in the URL, you’ll still have an empty string route value for the key “id”. And as it turns out, we use route values to bind to action method parameters.

In the scenario above, it just so happens that your model object happens to have a property with the same name, “Id”, as that route value, so the model binder attempts to set the value of the Id property to empty string, and since Id is a non-nullable int, we get a type conversion error.

This wouldn’t be so bad if “Id” wasn’t such a common name for properties. ;)

In ASP.NET MVC 2 RC 2, we added an MVC specific means to work around this issue via the new UrlParameter.Optional value. If you set the default value for a URL parameter to this special value, MVC makes sure to remove that key from the route value dictionary so that it doesn’t exist.

Thus the fix to the above scenario is to change the default route to:

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

With this in place, if there’s no ID in the URL, there won’t be a value for ID in the route values and thus we’ll never try to set a property named “Id” unless you have a form field named “Id”.

Note that this should be the default in the project templates for ASP.NET MVC 2 RTM.

comments edit

As many of you have probably heard, the release candidate for Visual Studio 2010 was recently released containing immense performance improvements and tons of bug fixes.

Another thing that release contains is the release candidate for ASP.NET MVC 2. However, that release is not the latest release of ASP.NET MVC 2 as we recently released a second release candidate for ASP.NET MVC 2 in response to customer feedback.

I apologize for the confusion this may have caused, but we really felt it was important to have another release candidate for ASP.NET MVC to help verify that we were responding to feedback in the correct manner.

If you wish to have Visual Studio 2010 RC and ASP.NET MVC 2 RC 2 installed at the same time, it’s not a problem.

If you installed ASP.NET MVC 2 RC 2 Before installing VS 2010 RC

Believe it or not, you’re all set if you install them in this order.

When installing VS 2010 RC, the installation will detect that a newer version of the ASP.NET MVC runtime (aka the System.Web.Mvc assembly) is already installed and will not overwrite it with the older version included with VS 2010 RC.

Keep in mind that the project templates for VS 2010 will still be the slightly older ASP.NET MVC 2 RC project templates and not the RC 2 templates. Fortunately those templates haven’t changed too much between release candidates.

In this configuration, when you create a project using VS 2010 RC, even though the templates may be slightly older, the project will reference the newer System.Web.Mvc assembly.

If you are installing ASP.NET MVC 2 RC 2 After installing VS 2010 RC

In this case, there’s a tiny bit of work to do. The installer for ASP.NET MVC 2 RC 2 will block if an older version of the ASP.NET MVC 2 runtime is installed.

To remedy the situation, all you need to do is uninstall the ASP.NET MVC 2 runtime. In Add/Remove Programs dialog(also known as the Program and features dialog), this would be the entry named “Microsoft ASP.NET MVC 2”.

If you have an older version of MVC tooling/project templates for Visual Studio 2008 installed (named “Microsoft ASP.NET MVC 2 – Visual Studio 2008 Tools”), you’ll also need to uninstall that, but do not uninstall the MVC tooling for VS 2010.

add-remove-dialog 

At this point, you should only have Microsoft ASP.NET MVC 2 – Visual Studio Tools for VS 2010 installed. You may now run the installer for ASP.NET MVC 2 RC 2, which will put the runtime on your machine as well as tooling/project templates for VS 2008.

Hopefully this clears up some of the confusion and gets you going with VS 2010 RC and ASP.NET MVC 2 RC 2.

personal, code comments edit

UPDATE: We moved the date to February 24th.

The stars at night, are big and bright – clap clap clap clap – deep in the heart of Texas!

Hold onto your ten gallon hats, I’m visiting Texas for the first time! I’m very excited to visit the second largest state in the union. ;)

austin The purpose of my trip is to meet with some developers at Dell doing interesting things and to give a talk there as well.

But since I’ve heard such good things about the vibrant tech community in Austin, I am trying to make the most of my short trip. On Wednesday, February 24, I’ll be speaking at Austin .NET User Group meeting at 7:00 PM CSTat the Microsoft offices in Austin.So be sure to come by and say hello.

We moved it to the 24th so if you were planning on attending Martin Fowler’s talk on February 25th, you can see both! Unfortunately for me, I was hoping to see Martin speak, but I saw that they’ve closed registration.

I’m also going to visit my good friends at Headspring as well as Dovetail Software to jabber about technology and see what cool things they’re doing with ASP.NET and ASP.NET MVC.

And of course, a trip to Austin wouldn’t be complete without a night out on the town with Bellware. I expect chaos. ;)

Tags: austin, texas, .net user group

personal, code comments edit

One question that came up recently during my mid-year review is how am I measuring customer satisfaction with the products that I work on? For example, how can I measurably demonstrate that customers are happy with the work we are doing on ASP.NET MVC and that my team is responding to customer feedback?

Umm, I can’t?

At least not right now in a measurable manner. I don’t have any such metric and I’m not sure how reliable any metric I might come up with will be.

rulerBut perhaps that’s simply due to a lack of imagination on my part.

In the past, we’ve tried various unscientific online twitter polls. One thing we did was ask the world for a list of ASP.NET pain points and compile them into one big list. Then at the end of the product cycle, we could show that we were able to address 10% of those pain points.

Personally, I feel that’s a bit contrived, but if it satisfies the muckety mucks, it works for me. ;)

Well actually, I’m not satisfied with that. At least not until I’ve tried to come up with something better. So I started thinking about what are ways to measure this and apart from simply using SurveyMonkey, I’ve sort of hit a wall.

This is where I ask you the question. If you were in my shoes what would you do? How have you quantitatively measured your effectiveness in responding to customer input? I’d love to hear your ideas.

UPDATE 2/4 1:06 AM By the way, I didn’t mean to give the impression that this is the only metric I need to collect. This is just the one I need help with. I think the number of downloads, installations, and clients is a much more important metric. However, some of those numbers are hard to collect with any accuracy. That’s why I think some of these other metrics might be useful as corroborating evidence. At least that’s my theory.

Tags: project management, metrics

asp.net, code, asp.net mvc comments edit

UPDATE: This blog post is out-of date. We released the RTM of ASP.NET MVC 2 in March 2010. Read about it here.

Today I’m pleased to announce the availability of Release Candidate 2 for ASP.NET MVC 2.

thumbs-upAfter receiving feedback from our last release candidate back in December, we decided it would be prudent to have one more release candidate that incorporated the feedback. You can read the release notes for everything that changed, there’s not a whole lot.

The biggest change in this release was described by Brad Wilson in his blog post on Input Validation vs. Model Validation in ASP.NET MVC. Also included in this release are an assortment of bug fixes and performance improvements.

The window to provide feedback on this release is going to be very short as we are closing in on the RTM. If you want to provide input into this release, please do take the bits for a spin as soon as possible. I’m pretty excited about this release as I can see the end of the tunnel fast approaching. :)

At this point, we’ll only be taking recall class bugs for ASP.NET MVC 2. All other bug reports will be filed against ASP.NET MVC 3. Sometime in the near future, I’ll start sharing some of our planning around that. How exciting!

Oh, and if you missed the link to the Download Details page at the beginning of this post, here it is again.

We also posted the source code and futures assembly on our Codeplex Site.

code comments edit

It’s that time of year again when a young man’s thoughts turn to Las Vegas! Yep, it’s another year and another Mix conference (March 15-17, 2010), but this time they’ve changed locations to Mandalay Bay.

mandalay-bay

It looks like my prediction that the Mix conferences would end at Mix 09 did not pan out. ;)

As I did last year, I’ll be giving two talks at Mix 10 this year, one of them with the irrepressible Scott Hanselman.

What’s new in ASP.NET MVC 2

Come see and hear about the latest innovations in ASP.NET MVC 2 and the tooling support in Visual Studio 2008 and 2010. We’ll introduce you to a range of productivity (and extensibility) enhancements such as template helpers, model validation, and the new “Areas” feature, which enhances the team development of large Web sites. With template helpers you can get your website up and running for any data entity type without having to create UI. With improved server side validation and brand new client side validation support, your business data model can define the behavior of your application automatically. All this and more!

THE HAAHA SHOW: ASP.NET MVC SECURITY WITH HAACK AND HANSELMAN

Join Phil Haack and Scott Hanselman for this dynamic and unusual security session. The HaaHa brothers will take turns implementing features on an ASP.NET MVC Web Site. Scott will write a feature, and Phil will exploit it and hack into the system. We’ll analyze and discuss the exploits live on stage and then close them one by one. You’ll learn about XSS, CSRF, JSON Hijacking and more. Is *your* site safe from the Haack?

Mix10_LoveTheWeb_blk_240 Lest you think Scott and I have big heads and self inflated egos, I should explain the title of the second talk. Last year Scott and I were supposed to give a talk together at the Norwegian Developer’s Conference, but we were very late in submitting a talk and abstract. As in, we had nothing a week before the conference.

So Rune (the organizer) simply put in the title “The Haacked and Hanselman Show” as a placeholder, and it stuck. This talk will follow in the footsteps of that talk, but in some cases it may be more difficult to give because of security improvements in ASP.NET.

Right now I’m prepping for the talk by attempting to discover a 0 day exploit that I can reveal live as the finale for the talk. Won’t that be fun! ;)

(Yes, I’m kidding you humorless security experts out there)

Anyways, if you’re going to be at Mix, be sure to come by and say hello! Don’t be shy.

Technorati Tags: mix10,mix,las vegas

asp.net, asp.net mvc, code comments edit

UPDATE: THIS POST IS DEPRECATED!!! I’ve updated the original post for editable routes to work in medium trust and not require a full app domain reload like this approach does. I think that approach may supersede this approach until I learn otherwise. :)

Yesterday I wrote about a technique using dynamic compilation to allow editing routes after you’ve deployed an application without having to manually recompile your application.

I made use of a FileSystemWatcher to monitor a Config directory and dynamically recompiled code when the code file changed. This has one advantage over using the App_Code directory in that the whole App Domain doesn’t need to get recycled when you make changes to your routes.

Today, my co-worker David Ebbo (who’s a master at ASP.NET Build and Compilation system) pointed out one gaping flaw with my approach. It doesn’t work in Medium Trust because the FileSystemWatcher class demands full trust. Doh!

For many business systems, that may not be a concern. But for my blog engine, it’s a huge concern. There are workarounds to the FileSystemWatcher issues, but I decided to take the easy way out and use the App_Code directory approach, since it handles all that crufty logic for watching the file system for me.

In this case, I simply added a new folder named App_Code to my project and copied Routes.cs to that folder.

routes-in-app_code

I then added a new method to RouteRegistrationExtensions. (Note that the actual code has some null reference checking which I omitted here.)

public static void RegisterAppCodeRoutes(this RouteCollection routes) {
  var type = BuildManager.GetType("Routes", false/*throwOnError*/);
  var registrar = Activator.CreateInstance(type) as IRouteRegistrar;
  registrar.RegisterRoutes(RouteTable.Routes);
}

So instead of the method I wrote in my previous post, I call this method. The nice thing here is that this method doesn’t have to worry about attaching a FileSystemWatcher or handling events and reloading routes.

Any time the Routes.cs file is changed, the entire App Domain is restarted and Application_Start is called again.

I want to also point out that a long while ago, I showed a different approach for editable routes using IronRuby that you might be interested in.

You can download the sample project here which includes both methods for doing editable routes.

asp.net mvc, asp.net, code, nuget comments edit

UPDATE: 2011/02/13: This code is now included in the RouteMagic NuGet package! To use this code, simply run Install-Package RouteMagic within the NuGet Package Manager Console.

In general, once you deploy your ASP.NET MVC application, you can’t change the routes for your application without recompiling the application and redeploying the assembly where your routes are defined.

routesThis is partly by design as routes are generally considered application code, and should have associated unit tests to verify that the routes are correct. A misconfigured route could seriously tank your application.

Having said that, there are many situations in which the ability to change an application’s routes without having to recompile the application comes in very handy. This is the situation I find myself in as I build a blog engine where the folks who will install may want to tweak the routes without having to recompile the blog’s source code.

In this post, I’ll demonstrate an approach that’ll allow you to define your routes in a content file as code(no XML here!) which you deploy with your application as in the screenshot.

Routes File In
Soultion

In my implementation, you need to place the routes in a Config folder in your web root. Note that I used Visual Studio’s Properties dialog to mark the file’s Build Action as “Content” so that it’s not compiled into my application.

Properties

What this means is that the code in the Routes.cs file is not compiled with the application. Instead, we will dynamically compile it. First, let’s look at the contents of that file. It shouldn’t be too surprising.

using System.Web.Mvc;
using System.Web.Routing;
using EditableRoutesWeb;

public class Routes : IRouteRegistrar
{
  public void RegisterRoutes(RouteCollection routes)
  {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
      "Default",
      "{controller}/{action}/{id}",
      new { controller = "Home", action = "Index", id = "" }
    );
  }
}

One thing you’ll notice is that this class implements an interface named IRouteRegistrar. This is an interface I created and added to my web application (though it could be defined in another assembly).

The code in Global.asax.cs for this application simply calls an extension method I wrote.

protected void Application_Start()
{
  AreaRegistration.RegisterAllAreas();
  RouteTable.Routes.RegisterRoutes("~/Config/Routes.cs");
}

It’s the code in this extension method where the real magic happens.

Before I show the code, there are two concepts at work here that make this work. The first is using the BuildManager to dynamically create an assembly from the Routes.cs file. From that assembly, we can create an instance of the type Routes and cast it to IRouteHandler.

var assembly = BuildManager.GetCompiledAssembly("~/Config/Routes.cs");
var registrar = assembly.CreateInstance("Routes") as IRouteRegistrar;

Once we have an instance of a route registrar, we can call RegisterRoutes on that instance.

The second concept is being able to get notification when the Routes.cs file changes. The clever trick that David told me about is using the ASP.NET Cache object to do that. When you add an item to the cache, you can give it a cache dependency pointing to a file and a method to call when the cache is invalidated.

With those two pieces, we can add a cache dependency pointing to Routes.cs and a callback method which will reload the routes when Routes.cs is changed.

Here’s the full listing for RouteRegistrationExtensions.

public static class RouteRegistrationExtensions
{
  public static void RegisterRoutes(this RouteCollection routes, 
      string virtualPath)
  {
    routes.ReloadRoutes(virtualPath);
    ConfigFileChangeNotifier.Listen(virtualPath, 
      vp => routes.ReloadRoutes(vp));
  }

  static void ReloadRoutes(this RouteCollection routes, string virtualPath)
  {
    var assembly = BuildManager.GetCompiledAssembly(virtualPath);
    var registrar = assembly.CreateInstance("Routes") as IRouteRegistrar;
    using(routes.GetWriteLock())
    {
      routes.Clear();
      registrar.RegisterRoutes(routes);
    }
  }
}

This uses a class called ConfigFileChangeNotifier which is based on some code David wrote for Dynamic Data.

public class ConfigFileChangeNotifier
{
  private ConfigFileChangeNotifier(Action<string> changeCallback)
    : this(HostingEnvironment.VirtualPathProvider, changeCallback)
  { 
  }

  private ConfigFileChangeNotifier(VirtualPathProvider vpp, 
      Action<string> changeCallback) {
    _vpp = vpp;
    _changeCallback = changeCallback;
  }

  VirtualPathProvider _vpp;
  Action<string> _changeCallback;

  // When the file at the given path changes, 
  // we'll call the supplied action.
  public static void Listen(string virtualPath, Action<string> action) {
    var notifier = new ConfigFileChangeNotifier(action);
    notifier.ListenForChanges(virtualPath);
  }

  void ListenForChanges(string virtualPath) {
    // Get a CacheDependency from the BuildProvider, 
    // so that we know anytime something changes
    var virtualPathDependencies = new List<string>();
    virtualPathDependencies.Add(virtualPath);
    CacheDependency cacheDependency = _vpp.GetCacheDependency(
      virtualPath, virtualPathDependencies, DateTime.UtcNow);
      HttpRuntime.Cache.Insert(virtualPath /*key*/,
        virtualPath /*value*/,
        cacheDependency,
        Cache.NoAbsoluteExpiration,
        Cache.NoSlidingExpiration,
        CacheItemPriority.NotRemovable,
        new CacheItemRemovedCallback(OnConfigFileChanged));
  }

  void OnConfigFileChanged(string key, object value, 
    CacheItemRemovedReason reason) {
    // We only care about dependency changes
    if (reason != CacheItemRemovedReason.DependencyChanged)
      return;

    _changeCallback(key);

    // Need to listen for the next change
    ListenForChanges(key);
  }
}

With this in place, you can now change routes within the Routes.cs file in the Config directory after you’ve deployed the application. Note that technically, a recompilation is happening, but it’s happening dynamically at runtime when the file changes and there’s no need to restart your entire App Domain, which is one benefit of this approach over using the code in App_Code.

If you want to try this code out, you can download a sample project here. The sample app is compiled against ASP.NET MVC 2 RC, but the same principles and code can be used with an ASP.NET MVC 1.0 application. In fact, it can also be used in an ASP.NET 4 Web Forms application since we now support page routing.

Note, if you want to see the old version of this code, I’ve archived it here.

Tags: aspnetmvc, routes, buildmanager, assembly, filesystemwatcher

asp.net comments edit

This is the second job posting in two days for positions on the ASP.NET team, how exciting! This one is a developer position for upcoming graduates.

Before I continue, I need to fulfill a promise to a co-worker to include a barely relevant stock photograph in this blog post.

Good, with that out of the way, allow me to continue.

If you are in college or grad school, graduating in 2010, and are looking for a great job writing code for ASP.NET, we have a position available!  Send me your resume and I’ll forward it along to the proper people.

My email address is philha at (my company).com where (my company) is Microsoft. If you can’t figure out based on that where to email your resume, you probably shouldn’t bother applying ;)

Once again, this is for college or graduate school students graduating in 2010. If you’re not an upcoming graduate, consider we also have a QA position available.

I look forward to seeing you roam the halls of building 42.

Technorati Tags: aspnet,jobs,careers,hiring

asp.net comments edit

The ASP.NET Team is still looking for that QA person out there who shares our passion for technology and improving the means by which software is made.

Keep in mind that the QA position on our team is not someone who mindlessly follows a script hoping by sheer random luck to find bugs. Oh no no no my friends.

This is considered a software development position in which you will be responsible for improving the processes and tools we have in place for ensuring quality software. You’d be involved in improving the quality of all phases of product development as a valued member of a feature crew. Think broadly about the idea of software quality. Your role is to question whether we’re building the right product as much as it is to test the product once it is built.

Matt Osborn, a member of the ASP.NET MVC feature crew has a description of the position.

That’s right you read the title right the ASP.NET QA team is once again hosting ninja try outs. We are looking for someone who is able to help evolve the team processes, improve our tooling, and join us in the trenches as we test one of the best technologies out there. Overall the team is responsible for ASP.NET WebForms, ASP.NET MVC, Microsoft AJAX, and a whole slew of other technologies. Our technologies can be found in numerous large scale web sites such as MySpace.com, Dell.com andDeals.Woot.com.

Once again we would like to find someone that shares our passion for our technology and our trade and someone that has spent sometime in the trenches. So if this sounds like something you’re interested in polish your nun chucks, practice your disappearing skills, and slice your way through our job posting. In the meantime while your sword is out for sharpening send Mark Berryman (one of our managers) an email.

PS: If you listen to the Coding QA podcast you can get a good feel for what it is like being the frontline testing ninjas for ASP.NET. We had a great turn out last time and please feel free submit your resume again it can’t hurt.

Keep in mind we’re not looking for “test” ninjas as in “sample” ninjas. We’re looking for the real deal, brimming with throwing stars of bug finding. Ok, the analogy breaks down a bit there. But you get the picture.

And before I forget, here’s the direct link to the job description on the Microsoft careers site.

subtext, code comments edit

This blog is experiencing technical difficulties. Do not adjust your browser.

Hi there. If you’ve tried to visit this blog recently you might have noticed it’s been down a lot in the last two days. My apologies for that, but hopefully you found what you needed via various online web caches.

I’ve been dogfooding the latest version of Subtext and as CodingHorror points out, dogfood tastes bad.

I’ve done a lot of testing on my local box, but there are a class of bugs that I’m only going to find on a high traffic real site, and boy have I found them!

Some of them might be peculiar to my specific data or environment, but others were due to assumptions I made that were wrong. For example, if you use ThreadPool.QueueUserWorkItem to launch a task, and that task throws an unhandled exception, that can bring your entire App Domain down. Keep that in mind if you think to use that method for a fire-and-forget style task.

In any case, the point of this post is to say that we’re not going to release the next version of Subtext until it’s rock solid. My blog going down occasionally is the cost I’m incurring in order to make sure the next version of Subtext is a beast that won’t quit.

asp.net, asp.net mvc comments edit

Note: This describes the behavior of ASP.NET MVC 2 as of the release candidate. It’s possible things might change for the RTM.

When using areas in ASP.NET MVC 2, a common problem you might encounter is this exception message.

The controller name ‘Home’ is ambiguous between the following types: \ AreasDemoWeb.Controllers.HomeController \ AreasDemoWeb.Areas.Blogs.Controllers.HomeController

This message is telling you that the controller factory found two types that match the route data for the current request. Typically this happens when you have a controller of the same name in an area and in the main project.

For example, in the screenshot below, notice that we have a HomeController in the main Controllers folder as well as in the Blogs area.

area-with-ambiguous-controller

If you make a request for the area such as /Blogs/Home, you’ll find that everything works hunky-dory. However, if you make a request for the root HomeController, such as /Home, you’ll get the ambiguous controller exception.

Why is that?

When you register routes for an area, they get a namespace associated with each route. That ensures that only controllers within the namespace associated with that area can fulfill the request. Thus the request that matches an area will have that namespace and the namespace is used to disambiguate controllers.

But by default, the routes in the main application don’t have a namespace associated with them. That means the controller factory will scan all types looking for a match, and in this case finding two types which match the controller name “Home”.

The Fix

There are two very simple workarounds. The simplest falls in the “If it hurts, stop doing that” camp which is to simply avoid naming two controllers the same name.

For many situations, this is not a satisfactory answer. The other workaround, as you might guess from my explanation of why this happens, is to give the route in the main application a specific namespace. Here’s an example of the default route in Global.asax.cs which has the fix.

public static void RegisterRoutes(RouteCollection routes)
{
  routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

  routes.MapRoute(
    "Default",                                              // Route name
    "{controller}/{action}/{id}",                           // URL
    new { controller = "Home", action = "Index", id = "" }, // Defaults
    new[]{"AreasDemoWeb.Controllers"}                       // Namespaces
  );
}

In the code above, I added a fourth parameter which is an array of namespaces. The controllers for my project live in a namespace called AreasDemoWeb.Controllers.

Follow Up

In a follow-up post, I’ll walk through more details about areas and how namespaces play into routing and controller lookup. For now, I hope this gets you unstuck if you’ve run into this problem before.

code comments edit

Confirmation dialogs were designed by masochists intent on making users of the web miserable. At least that’s how I feel when I run into too many of them. And yes, if you take a look at Subtext, you can see I’m a perpetrator.

Well no longer!

I was managing my Netflix queue recently when I accidentally added a movie I did not intend to add (click on the image for a larger view).

netflix-queue Naturally, I clicked on the blue “x” to remove it from the queue and saw this.

netflix-queue-deleted Notice that there’s no confirmation dialog that I’m most likely to ignore questioning my intent requiring me to take yet one more action to remove the movie. No, the movie is removed immediately from my queue just as I requested. I love it when software does what I tell it to do and doesn’t second guess me!

But what’s great about this interface is that it respects that I’m human and am likely to make mistakes, so it offers me an out. The row becomes grayed out, shows me a status message, and provides a link to undo the delete. So if I did make a mistake, I can just click undo and everything is right with the world. Very nicely done!

I started to get curious about how they did it and did not find any existing jQuery plugins for building this sort of undoable interface, so I decided this would be a fun second jQuery plugin for me to write, my first being the live preview plugin.

The Plugin

Much like my jQuery hide/close link, the default usage of the plugin relies heavily on conventions. As you might expect, all the conventions are easily overriden. Here’s the sample HTML for a table of comments you might have in the admin section of a blog.

<table>
    <thead>
        <tr>
            <th>title</th>
            <th>author</th>
            <th>date</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>This is an interesting plugin</td>
            <td>Bugs Bunny</td>
            <td>12/31/2009</td>
            <td><a href="#1" class="delete">delete</a></td>
        </tr>
        <tr>
            <td>No, it's a bit derivative. But nice try.</td>
            <td>Derrida</td>
            <td>1/1/2010</td>
            <td><a href="#2" class="delete">delete</a></td>
        </tr>
        <tr>
            <td>Writing sample data is no fun at all.</td>
            <td>Peter Griffin</td>
            <td>1/2/2010</td>
            <td><a href="#3" class="delete">delete</a></td>
        </tr>
    </tbody>
</table>

And the following is the code to enable the behavior.

$(function() {
    $('a.delete').undoable({url: 'http://example.com/delete/'});
});

By convention, when one of the delete links is clicked, the value in href attribute is posted as form encoded data with the key “id” to the specified URL, in this case http://example.com/delete/.

If you have more form data to post, it’s quite easy to override how the form data is put together and send whatever you want. The following examples pulls the id from a hidden input and sends it with the form key “commentId”.

$(function() {
  $('a.delete').undoable({
    url: 'http://example.com/delete/',
    getPostData: function(clickSource, target) {
      return {
        commentId: target.find("input[type='hidden'][name='id']").value(),
        commentType: 'contact'
      };
    }
  });
});

When the data is posted to the server, the server must respond with a JSON object having two properties, subject and predicate. For example, in ASP.NET MVC you might post to an action method which looks like:

public ActionResult Delete(int id) {
  // Delete it
  return Json(new {subject = "The comment", predicate = "was deleted"});
}

The only reason I broke up the response message into two parts is to enable nice formatting like Netflix’s approach.

This of course is is easily overridden. For example, it may be simpler to simply return  I can override the formatStatus method to expect other properties in the response from the server. For example, suppose you simply want the server to respond with one message property, you might do this.

$(function() {
  $('a.delete').undoable({
    url: 'http://example.com/delete/',
    formatMessage: function(response) {
      return response.message;
    }
  });
});

I wrote the plugin with the TABLE scenario in mind as I planned to use it in the comment admin section of Subtext, but it works equally well with other elements such as DIV elements. For example, the user facing comments of a blog are most likely in a list of DIV tags. All you need to do to make this work is make sure the DIV has a class of “target” or override the getTarget method.

If you want to see more examples of how to use the plugin in various scenarios, check out the jquery.undoable demos page.

I need your help!

I really hope some of you out there find this plugin useful. Writing these plugins has been a great learning experience for me. I found the following two resources extremely valuable.

  • jQuery Plugin Authoring Guide This is the beginners guide on the jQuery documentation page. I found it to be very helpful in learning the basics of plugin development.
  • A Plugin Development Pattern by Mike Alsup. Mike outlines a pattern for writing jQuery plugins that has worked well for him based on his extensive experience. I tried to follow this pattern as much as I could.

However, I still feel there’s room to improve. I’m not sure that I fully grasped all the tips and wrote a truly idiomatic usable extensible clean jQuery plugin. So if you have experience writing jQuery plugins, please do enumerate all the ways my plugin sucks.

If you simply use this plugin, please tell me what does and doesn’t work for you and how it could be better. I’m really having fun writing these plugins and would find your constructive feedback very helpful.

The Source

As with my last plugin, the source is hosted on GitHub. Git is another tool I’m learning. I can’t really make a judgment until I use it on a project where I’m collaborating with others *hint* *hint*. :) Please do fork it and provide patches and educate me on writing better plugins. :)

code comments edit

UPDATE: 12/30 I had transposed the rgb colors. I corrected the function.

I’ve been distracted by a new jQuery plugin that I’m writing. The plugin has certain situations where it sets various background and foreground colors. You can have it set those styles explicitly or you can have it set a CSS class, and let the CSS stylesheet do the work.

color-wheelI’m writing some unit tests to test the former behavior and ran into an annoying quirk. When testing the color value in IE, I’ll get something like #e0e0e0, but when testing it in FireFox, I get rgb(224, 224, 224).

Here’s a function I wrote that normalizes colors to the hex format. Thus if the specified color string is already hex, it returns the string. If it’s in rgb format, it converts it to hex.

function colorToHex(color) {
    if (color.substr(0, 1) === '#') {
        return color;
    }
    var digits = /(.*?)rgb\((\d+), (\d+), (\d+)\)/.exec(color);
    
    var red = parseInt(digits[2]);
    var green = parseInt(digits[3]);
    var blue = parseInt(digits[4]);
    
    var rgb = blue | (green << 8) | (red << 16);
    return digits[1] + '#' + rgb.toString(16);
};

Now, I can compare colors like so.

equals(colorToHex('rgb(120, 120, 240)'), '#7878f0');

I hope you find this useful. :)

code comments edit

UPDATE (12/26): I updated this post to use the href instead of the rel attribute

It’s Christmas day, and yes, I’m partaking in the usual holiday fun such as watching Basketball, hanging out with the family and eating our traditional Alaskan king crab Christmas dinner. But of course it wouldn’t be a complete day without writing a tiny bit of code!

code

Today I’ve been working on improving the UI here and there in Subtext. One common task I run into over and over is using an anchor tag to trigger the hiding of another element such as a DIV. It happens so often that I get pretty tired of hooking up each and every link to the element it must hide. Being the lazy bastard that I am, I thought I’d try to come up with a way to do this once and for all with jQuery and a bit of convention.

Here’s what I came up with. The following HTML shows a DIV element with an associated link that when clicked, should hide the DIV.

<div id="hide-this">
    This here DIV will be hidden when you click on 
    the link
</div>
<a href="#hide-this" class="close">This is the link that hides the DIV</a>

The convention here is that any anchor tag with a class “close” is going to have its click event hooked up to close another element. That element is identified by the anchor tag’s rel href attribute, which contains the id of the element to hide. This was based on a suggestion by a couple of commenters to the original version of this post where I used a rel attribute. I like this much better for two reasons:

  • The href value is a hash which is already in the correct format to be a CSS selector for an ID.
  • I’m not using the href value in the first place, so might as well make use of it.

Yeah, this is probably an abuse of this attribute, but in this case it’s one I can live with due to the benefits it produces. The rel attribute is supposed to define the relationship of the current document to the document referenced by the anchor tag. Browsers don’t do anything with this attribute, but search engines do as in the case with therel value of “no-follow”~~.~~

However in this case, I feel my usage is in the spirit of this attribute as I’m defining the relationship of the anchor tag to another element in the document. Also, search engines are going to ignore the value I put in there unless the id happens to match a valid value, so no animals will be harmed by this.

Now I just need a little jQuery script to make the magic happen and hook up this behavior.

$(function() {
    $('a.close').click(function() {
        $($(this).attr('href')).slideUp();
        return false;
    });
});

I happened to choose the slide up effect for hiding the element in this case, but you could choose the hide method or fadeOut if you prefer.

I put up a simple demo here if you want to see it in action.

I’m just curious how others handle this sort of thing. If you have a better way, do let me know. :)

Tags: jQuery, JavaScript

personal comments edit

Just wanted to wish you all a Merry Christmas, Happy Holidays, or a Happy whatever you are celebrating at this time of year. I hope you are spending it well with family and friends! :)

As you can see, I’m still hard at work watching the kids on paternity leave.

Playing Call of Duty Modern Warfare 2 With
Mia

My brother is a drug dealer and the name of the drug is Call of Duty: Modern Warfare 2 for the X-Box 360. I’m totally hooked right now, and I don’t usually get so hooked on games.

At least I am managing to still get some fresh air outside and enjoy the weather. Here I’m walking with my wife (taking the photo), my mother, my son, and my brother.

033 Of course, every walk we go on ends up with me lugging my son around.

042

I wouldn’t trade it for anything. :)

asp.net, asp.net mvc, code comments edit

When we released ASP.NET MVC 2 Beta back in November, I addressed the issue of support for Visual Studio 2010 Beta 2.

Unfortunately, because Visual Studio 2010 Beta 2 and ASP.NET MVC 2 Beta share components which are currently not in sync, running ASP.NET MVC 2 Beta on VS10 Beta 2 is not supported.

The release candidate for ASP.NET MVC 2 does not change the situation, but I wasn’t as clear as I could have been about what the situation is exactly. In this post, I hope to clear up the confusion (and hopefully not add any more new confusion) and explain what is and isn’t supported and why that’s the case.

why

Part of the confusion may lie in the fact that ASP.NET MVC 2 consists of two components, the runtime and what we call “Tooling”. The runtime is simply the System.Web.Mvc.dll which contains the framework code which you would reference and deploy as part of your ASP.NET MVC application.

The Tooling consists of the installer, the project templates, and all the features in Visual Studio such as the Add View and Add Area dialogs. Much as ASP.NET 4 is different from Visual Studio 2010, the ASP.NET MVC 2 tooling is different from the runtime. The difference is that we primarily release both components in one package.

The reason I bring this up is to point out that when I said that ASP.NET MVC 2 RC is not supported on machines with Visual Studio 2010 Beta 2, I’m really referring to the tooling.

The ASP.NET MVC 2 RC runtime is fully supported with the ASP.NET 4 runtime. As I mentioned before, we are not compiling a different version of the runtime for ASP.NET 4. It’s the same runtime. So you can create an ASP.NET empty web application project, for example, add in the RC of System.Web.Mvc.dll as a reference, and go to town.

The problem of course is that you won’t have the full tooling experience at your disposal in VS2010 such as project templates and dialogs. This is definitely a pain point and very unfortunate.

Why don’t we ship updated installers for Visual Studio 2010 Beta 2?

This is a fair question. What it comes down to is that this would add a lot of extra work for our small team, and we’re already working hard to release the core features we have planned for this release.

Add this extra work and something would have to give. It would have to come at the cost of feature work and bug fixes and we felt those were a higher priority than temporary support for interim releases of VS2010.

Why would this add overhead? Eilon Lipton, lead developer on the ASP.NET MVC feature team, covers this well in his comment on my last post.

Regarding Visual Studio 2010 and .NET 4 support, that is unfortunately not a feasible option. The most recent public release of VS2010 and .NET 4 is Beta 2. However, our internal builds of MVC 2 for VS2010 and .NET 4 depend on features that were available only after Beta 2. In other words, if we released what we have right now for VS2010 and .NET 4 then it wouldn’t even run.

We are constantly syncing our internal builds with the latest builds of Visual Studio 2010. As Eilon points out, to support VS 10 Beta 2, we’d have to have two separate builds for VS10, one for Beta 2 and one for the latest internal build. Keep in mind, this is on top of the build for Visual Studio 2008 we’re doing.

Trying to sync our tooling against two different versions of Visual Studio is hard enough. Doing it against three makes it much more difficult.

As I mentioned before, the ASP.NET MVC 2 project schedule isn’t aligned with the Visual Studio 2010 schedule exactly. Heck, when ASP.NET MVC 1.0 was shipped, work on VS 10 was already underway, so we were playing catch-up to catch the VS 10 ship train. Thus when the Beta 2 was code complete, we weren’t done with our Beta. When we were done with Beta, we were already building our tools against a newer build of VS10. The same thing applies to the RC.

What about Visual Studio 2010 RC?

Funny thing is, since I’ve been on leave, I pretty much found out that we were even having a public Release Candidate for Visual Studio 2010 the same time you probably did via ScottGu’s Blog post on the subject.

The good news is that the Visual Studio 2010 Release Candidate will include a newer version of ASP.NET MVC 2. We’re still working out the details of which exact version we will include, though I’d really like it to be the RC of ASP.NET MVC, assuming the logistics and schedule line up properly.

And of course, the Visual Studio 2010 RTM will include the ASP.NET MVC 2 RTM and at that point, all will be well with the world as installing tooling for ASP.NET MVC 2 will be supported on both VS 2008 and VS 2010 at the same time.

So again, we do understand this is an unfortunate situation and apologize for the inconvenience this may cause some of you. Aftor all, I feel the same pain! I want to install both versions on my machine just like you do. Fortunately, it’s only a temporary situation and will all be a bad memory when VS2010 RTM is released to the world. Thanks for listening.