views:

239

answers:

3

Hey guys,

I'm trying to switch our links over to T4MVC, and I'm having a small problem with parameters that aren't part of an action's signature. We have a route that goes something like this:

http://www.mydomain.com/{fooKey}/{barKey}/{barID}

==> leads to BarController.Details(barID).

fooKey and barKey are only added to the links for SEO purposes. (since bar is a child entity of foo, and we want to represent that hierarchy in the URL)

Up until now, we would use

<% =Html.ActionLink(bar.Name, "Details", "Bar", new {barID = bar.ID, fooKey = bar.Foo.Key, barKey = bar.Key}, null)%>

And that would lead us to BarController.Details(barID), while keeping fooKey and barKey in the URL.

Now that we started with T4MVC, we tried changing it to

<% =Html.ActionLink(bar.Name, MVC.Bar.Details(bar.ID), null)%>

Since barKey and fooKey are not part of the Details action signature, they are no longer visible in the URL.

Is there a way around this without having to add these parameters to the action signature?

+1  A: 

Look in the generated code inside of T4MVC.cs. There are the html helper extensions that take an ActionLink. Your going to have to write an overload that takes another set of route values and combines them with the ActionResult.GetRouteValueDictionary().

    public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, ActionResult result, IDictionary<string, object> htmlAttributes) {
        return htmlHelper.RouteLink(linkText, result.GetRouteValueDictionary(), htmlAttributes);
    }
jfar
A: 

Thanks jfar!

Here is the code I used, just in case anyone needs it. It could use a refactoring job, but it works

public static MvcHtmlString ActionLink<T>(this HtmlHelper<T> htmlHelper, string linkText, ActionResult result,
                                              object extraRouteValues, object htmlAttributes)
    {
        RouteValueDictionary completeRouteValues = result.GetRouteValueDictionary();
        RouteValueDictionary extraRouteValueDictionary = new RouteValueDictionary(extraRouteValues);
        foreach (KeyValuePair<string, object> foo in extraRouteValueDictionary)
        {
            completeRouteValues.Add(foo.Key, foo.Value);
        }

        Dictionary<string, object> htmlAttributesDictionary = htmlAttributes != null ? htmlAttributes.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(htmlAttributes, null)) : null;

        return htmlHelper.RouteLink(linkText, completeRouteValues, htmlAttributesDictionary);
    }
Omer Rauchwerger
+5  A: 

Similar thing also came up on the T4MVC Forum (this thread). I think I'll go ahead and add support for it in T4MVC.

Actually, I just thought of an interesting way to solve this. The problem with adding an overload to pass extra arguments is that you'd need to add similar overloads to all the other T4MVC extension methods that take an ActionResult, which can get messy.

Instead, we can use a fluent approach to make this available everywhere with little effort. The idea is that you'll write:

<%= Html.ActionLink(
    bar.Name,
    MVC.Bar.Details(bar.ID)
        .AddRouteValues(new {fooKey = bar.Foo.Key, barKey = bar.Key}))%>

Or if you only needed to add one value:

<%= Html.ActionLink(
    bar.Name,
    MVC.Bar.Details(bar.ID)
        .AddRouteValue("fooKey", bar.Foo.Key))%>

Here is how AddRouteValues is implemented:

public static ActionResult AddRouteValues(this ActionResult result, object routeValues) {
    return result.AddRouteValues(new RouteValueDictionary(routeValues));
}

public static ActionResult AddRouteValues(this ActionResult result, RouteValueDictionary routeValues) {
    RouteValueDictionary currentRouteValues = result.GetRouteValueDictionary();

    // Add all the extra values
    foreach (var pair in routeValues) {
        currentRouteValues.Add(pair.Key, pair.Value);
    }

    return result;
}

public static ActionResult AddRouteValue(this ActionResult result, string name, object value) {
    RouteValueDictionary routeValues = result.GetRouteValueDictionary();
    routeValues.Add(name, value);
    return result;
}

It would be great if you could give this a try and let me know how that works for you.

thanks, David

David Ebbo
Works great, and a very elegant solution. Thanks, David!
Omer Rauchwerger