Snippet: Ruby-like Hashes in C#
The ASP.NET MVC team developed a URL routing engine, similar to the engine in Rails, which has made it’s way into the rest of ASP.NET. ScottGu has covered it a few times in the past, and this post has some good examples of it. Here’s a quick example:
routes.MapRoute("ShowProductByCategory", "Products/Show/{Category}/{id}",
new { controller = "Products", action = "Show" });
The third parameter is a set of default values used by the routing engine so that it knows what class, methods and context parameters to pass. It’s an anonymous type, which works nicely and allows for undefined parameter keys and values to be passed, and it would use reflection inside the routing tool to read them back out.
I personally find the syntax in Ruby on Rails a little more natural. Check out the Ruby Hash class, which allows you to pass key/value pairs inline. Here’s what routing in Rails looks like:
map.connect 'products/show/:category/:id', :controller => 'products', :action => 'show'
The syntax looks very familiar to C# Lambda Expressions, which made me wonder whether they could be used (or rather, abused) to provide a nicer syntax for URL routing. Here’s what I managed to get working:
MapRoute("Products/Show/{Category}/{ID}", controller => "Products", action => "Show");
The MapRoute method would need to be defined as:
static void MapRoute(string urlPattern, params Expression<Func<object, object>>[] parameterDefaults)
{
Console.WriteLine("Pattern: {0}", urlPattern);
foreach (var parameter in parameterDefaults.ReadAll<string>())
{
Console.WriteLine(" Default {0}: {1}", parameter.Key, parameter.Value);
}
}
The argument is a params array of Lambda expressions, each of which take an object (so that you can specify a key), and return an object (whatever value you specify). The ReadAll<T>() method being called is an extension method which turns the array into a dictionary of key/value pairs, which allows the method to access them:
public static Dictionary<string, TResult> ReadAll<TResult>(this IEnumerable<Expression<Func<object, object>>> hashArguments)
{
var results = new Dictionary<string, TResult>();
foreach (var expression in hashArguments)
{
results[expression.Parameters[0].Name] = (TResult)expression.Compile()(null);
}
return results;
}
This method shows why we’re using Expressions: we can access the parameters being passed to the method, and compile and invoke the expressions to extract the values. It’s readable, and involves no custom reflection code.
Be warned that it probably won’t work if the code is obfuscated; but then again, the current ASP.NET routing syntax probably wouldn’t either?
A few other ways you could abuse Lambda Expressions:
- Developers would be able to specify named parameters, which would make it much easier to read the calling code.
- You could create a wrapper to invoke any method using reflection, allowing you to specify just the parameters you want. Imagine how much easier Office applications would be in C# if you were able to use optional parameters.
So Phil, is it too late for an overload to the routing syntax?
Filed under: C#


Pure evil. Think of the electrons, Paul!
Cool stuff.
Off topic question: why do you strike-through the class names (Hash, Expression etc.)? looks weird on Feed readers.
Makes no difference to me Paul. The difference is so small and to me, is not worth a second look. Ruby like, ASP.NET MVC like….bit of a whatever moment. Not really fussed either way. There is no advantage IMHO either, its just a little different. No reason you can’t wack it into the MVC COntrib project
Hi Ken,
I apologize for that. I mark up the post using strikethrough, because Live Writer has a button for strike, but it doesn’t have a button for <code> tags. I use Ctrl+H for strikethrough because it’s quicker, then I flick into HTML view and find/replace “strike>” with “code>”. I broke it tonight though because I accidentally hit Publish before I did the find and repalce. I corrected it quickly, but your feed reader must have gotten the feed just before I republished.
I’ve been meaning to write an add-in for live writer, but last I checked you’re limited to links on the plugin sidebar (which aren’t as quick as ye olde Ctrl+H).
Paul
Just picking out one facet of your code here, Paul.
I’m surprised we don’t see more use of the “params” keyword in methods. A lot of methods that currently take an IEnumerable would be well served by at least providing an overload that accepts a params T[] instead. It makes for very readable code when you’re passing literals into a method rather than a single predefined collection.
And speaking of - I hope Phil reads this post, and I hope he reads this comment and provides the “params” version as an overload so we have the best of both worlds.
You can override the route classes. It is rather easy to do.
Take a look at the Regular expression route class that I wrote:
https://systems-300.stellarfinancial.com/pub/Bill/rev/1eed6f697eca
MapRoute is nothing more than an extension method that calls routes.Add; there is no reason you cannot create an overload for it.
Yes, please do think of the electrons. After the ASP.net MVC crew unveiled the syntax based on anonymous types they were soundly beaten into submission by those concerned about the reflection overhead and in the next release they exposed a version taking a dictionary. Given that a Ruby hash is just a dictionary, why not avoid a bunch of dancing around and just use the MapRoute variant that takes one.
I should point out that the reflection overhead of defining routes is not a concern as we only pay that tax *once*, during startup.
Once the routes have been defined, there is no more reflection involved. Think of it this way, defining routes in code at App_Start using reflection is still going to be faster than reading them from web.config.
As for MapRoute, Bill is correct in that it is an extension method that is part of MVC and not routing. So it’s technically not too late, but I’m not sure we’ll end up adding that particular one.
It’s pretty darn exotic when you think about it. When someone sees MapRoute(string, params Expression>) in intellisense, are they going to have any clue what to put in there without reading your blog post?
Granted, we have the same issue with MapRoute(string, object), but object is easier to wrap your head around than Expression>.
Having said that, who knows. Maybe for MVC 2 as people get more comfortable with expressions.
I spotted an interesting discussion of this topic here too: http://blog.bittercoder.com/PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx
It explains that the technique (same as yours) can be done with Func instead of Expression>. Not obvious at first, but makes sense and gives a nice performance boost.
I also liked these two posts, which suggest collection initializers as another way to go (don’t look quite so pretty tho…)
http://haacked.com/archive/2008/01/06/collection-initializers.aspx
http://blogs.msdn.com/madst/archive/2006/10/10/What-is-a-collection_3F00_.aspx