Using BuildUrlFromExpression() Method in Controllers

Tuesday, September 23, 2008 8:45 AM

BuildUrlFromExpression() helper method is a helpful function in ASP.NET MVC. You can use it to generate a link based on your routing entry. By using this helper method you can avoid hardcoded links in your views.

For example, if you have the following route entry:

routes.MapRoute(
   "BlogPostAlias", 
   "blog/{postAlias}",
   new { controller = "Blog", 
         action = "ShowSinglePost" });

All URLs with pattern blog/{postAlias} will be routed to Blog controller and ShowSinglePost action. You may recall that Blog controller is the name of the controller and ShowSinglePost action is the name of the method in Blog controller which handles the requests.

Now, let's assume that we have a scenario to render all post links in a sitemap view. To make it easy, let's start from the point where we already fetched all posts from data store, and saved it to Posts collection variable, ready to be rendered.

There are several ways that we can do to accomplish this. I will present two approaches here. First we can hard-coded the link, like the one displayed below:

<% foreach (var post in Posts) { %>
<a href='/blog/<%= post.Alias %>'>post.Title</a>
<% } %>

The problem with this approach is hard-coded blog word in the link. If later you change routing entry by replacing the blog word with other word, you also have to replace all hard-coded links with the new word.

The second approach is using the BuildUrlFromExpression() helper method to generate dynamic link based on routing table entries. In ASP.NET MVC Preview 5, this helper method is placed in the Microsoft.Web.Mvc namespace. Here is the code with BuildUrlFromExpression() helper method:

<% foreach (var post in Posts) { %>
<a href='<%= Html.BuildUrlFromExpression<BlogController>
   (bc=>bc.ShowSinglePost(post.Alias)) %>'>post.Title</a>
<% } %>

We can also use ActionLink() helper method here, but my focus here is on BuildUrlFromExpressionMethod() since it offers greater flexibility to use in broader scenarios.

Note that the BuildUrlFromExpression() method is available both as static method and extension method of the HtmlHelper class. In the example above, I use the second version.

The example above shows that we can use BuildUrlFromExpression() extension method easily inside a view by leveraging the HtmlHelper instance of the view called Html. How can we use that function outside the view? We may need this method in a controller instead of a view.

This is rather tricky problem since both versions of BuildUrlFromExpression() method require ViewContext object in certain way. The first version (static method) requires the ViewContext as parameter. The second version (extension method to HtmlHelper), requires an instance of HtmlHelper. To create a HtmlHelper we also need a ViewContext object.

Based on those facts, a ViewContext is required to use BuildUrlFromExpression() method. But logically do we really need a view to produce a dynamic link based on route table data? Certainly not. All we need is a route data and a way to fetch the data by performing inverse mapping from controller/action to a route table entry.

If we delve deep enough into the source code, the ViewContext is required to be passed as parameter when calling the RouteCollection.GetVirtualPath() method. However, the required actual parameter type is not ViewContext, but RequestContext. ViewContext is inherited from ControllerContext which is further inherited from RequestContext. So, based on this fact, in ASP.NET MVC Preview 5, taking a ViewContext object inside the BuildUrlFromExpression() method is questionable. Why don't we use ControllerContext instead of ViewContext? ControllerContext is also a subclass of RequestContext and it's available both inside controllers and views.

In fact when I copy the source code of BuildUrlFromExpression() static method, and change the first parameter type from ViewContext to ControllerContext, it can work as well. So, for now we can do this as temporary solution before ASP.NET MVC team changes the method or discovers the better way to do it.

So, as conclusion, BuildUrlFromExpression() method is very useful to generate links based on route table entries. However, in ASP.NET MVC Preview 5 implementation, it is restricted as it can only be used inside views. To use it inside controllers, we can copy source code and change the first parameter from ViewContext to ControllerContext.