Thanks for the useful advice, in the case of data feeds etc I would probably also opt to use a view as suggested.
However when creating DTOs that get converted to XML for use by XSLT (e.g. CategoryDTO has a URL property) I have no choice but to generate the urls within the controller action.
Although the ViewContext parameter in the call to LinkBuilder.BuildUrlFromExpression can be null, this will result in a new RequestContext being created from the current HttpContext in the underlying call to RouteCollection.GetVirtualPath (in the System.Web.Routing assembly). This will result in an exception if the method is called outside of a real HttpContext, e.g. during a unit test.
I ended up writing the following class (see below) which allows you to generate a url to an action using a RequestContext and Expression.
I'm dubious that LinkBuilder will ever require the properties of the ViewContext because the method that generates the link (RouteCollection.GetVirtualPath method in System.Web.Routing) requires a RequestContext. The RedirectToRouteResult in MVC (which runs "Controller side") also makes a call to GetVirtualPath with a RequestContext. I'm hoping that LinkBuilder.BuildUrlFromExpression will be changed in a future MVC release to use a RequestContext parameter instead so we can get rid of this work-around.
Cheers
Dan
1 using System;
2 using System.Linq.Expressions;
3 using System.Web.Mvc;
4 using System.Web.Routing;
5 using Microsoft.Web.Mvc.Internal;
6
7 namespace Whatever
8 {
9 /// <summary>
10 /// Creates url that can be used in a link to a controller action, based on an
11 /// Expression containing a call to the action with the required parameters
12 /// </summary>
13 /// <remarks>
14 /// This is based on the LinkBuilder.BuildUrlFromExpression method within MVC preview 4.
15 /// However the original method requires a ViewContext which is not available when the
16 /// method is invoked from within a Controller method.
21 /// </remarks>
22 public static class ControllerLinkBuilder
23 {
24 public static string GetActionUrl
25 (RequestContext context, Expression> action) where T : Controller
26 {
27 return BuildUrlFromExpression(context, action);
28 }
29
30 public static string BuildUrlFromExpression
31 (RequestContext context, Expression> action) where T : Controller
32 {
33 RouteValueDictionary values = ExpressionHelper.GetRouteValuesFromExpression
34 (action);
35 VirtualPathData vpd = RouteTable.Routes.GetVirtualPath(context, values);
36 return (vpd == null) ? null : vpd.VirtualPath;
37 }
38 }
39 }