My Technical Notes

Friday, 18 July 2014

Strongly-typed Html.ActionLink

Html.ActionLink is fraught with the possibility that either the controller or action or parameter name(s) have been changed. As a way of making sure that these are always valid, we can use a strongly typed lambda expression.

We first define an ActionLink method which takes in the linkText (a string) and a lambda expression representing the controller, action and parameter(s):

public static class HtmlActionExtensions
    public static MvcHtmlString ActionLink<T, U>(this HtmlHelper html, string linkName, Expression<Func<T, U>> action)
        var controllerName = action.Parameters.First().Type.Name.Replace("Controller", "");

        var methodExpr = (action.Body as System.Linq.Expressions.MethodCallExpression);

        var actionName = methodExpr.Method.Name;

        var parameters = new Dictionary<string, object>();

        if (methodExpr.Arguments.Any())
            var methodParams = methodExpr.Method.GetParameters();

            int counter = 0;
            foreach (var arg in methodExpr.Arguments)
                // see second answer of Eric Lippert here:

                var paramName = methodParams.ElementAt(counter).Name;
                var paramValue = GetValue(arg);

                parameters.Add(paramName, paramValue);

        var routeDict = new System.Web.Routing.RouteValueDictionary(parameters);
        return html.ActionLink(linkName, actionName, controllerName, routeDict, null);

    public static object GetValue(Expression member)
        return Expression.Lambda(member).Compile().DynamicInvoke();

Given a test action TestAction defined in the HomeController class:

public ActionResult TestAction(int id)
    return Content("test action called with id: " + id);

We can add to our view:

@Html.ActionLink("Test Action", (HomeController h) => h.TestAction(76))

No comments: