views:

645

answers:

3

I want to be able to map a route using a URL that doesn't conform to the {controller}/{action}/{id} format. The mapping looks like:

routes.CreateArea("Root", "MyApp.Web.Controllers",
    routes.MapRoute("Category-List", "Category/{category}", 
        new { controller = "Category", action = "List" }),
    routes.MapRoute("Default", "{controller}/{action}/{id}", 
        new { controller = "Home", action = "Index", id = "" })
);

Where I have a CategoryController with an action List(string category).

I was hoping to be able to use this in my view:

<%= Html.ActionLink<CategoryController>(
    c => c.List(category.UrlFriendlyName), 
    category.Name)%>

(line breaks added for readability)

All this produces is a link with href="". Removing the route from the "Root" area produces the correct result. Is it possible to use this type of mapping with the generic ActionLink helper or do I have to resort to RouteLink or something similar with hard coded values?

I also tried the following with no success:

<%= Html.ActionLink(category.Name, "List", "Category", 
new { category = category.UrlFriendlyName }) %>
A: 

By sight, what you have written looks correct for producing the URL you want. Have you tried using the non-strongly typed Html.ActionLink method to see if that works?

Garry Shutler
Wasn't quite sure what it should look like but this didn't work: <%= Html.ActionLink(category.Name, "List", "Category", new { category = category.UrlFriendlyName }) %>
roryf
That looks about right, haven't used them for a while though! Have you tried setting a default for category as well?
Garry Shutler
A: 

I don't see anything in your call to ActionLink which would cause the routing system to realize which route to use. Instead, I would recommend using Html. RouteLink, which allows you to specify a route by name. This will ensure that the correct route is matched.

Update: are you doing a cross-area link? (In other words, is the area you're linking to different than the area containing the link?) If so, you must specify the area in your call to ActionLink/RouteLink. If not, does RouteLink work?

I don't think this is what is causing the problem, but I noticed that your Category-List route has no constraints, and I think it should probably be constrained to the Category controller.

Craig Stuntz
It will hit his rule due to the fact that it's a link using the Category controller and the List action.
Garry Shutler
No, that's entirely wrong, as evidenced by the fact that it doesn't work. Why? Defaults don't cause a route to match; the url pattern does. Defaults are used when the token isn't supplied, not when it is.
Craig Stuntz
Defaults do cause a route to match. Defaults are overridden by the values you provide though. If you override Category with Category it still matches.
Garry Shutler
Garry is correct, problem was the route was inside an area, without that the ActionLink works fine.
roryf
Defaults don't so much cause the route to match as prevent the lack of a value from making the match fail. With the default, the route he has will match *anything.* There is nothing in the ActionLink which says, "match this route and no others." RouteLink, OTOH, does that.
Craig Stuntz
RE: Areas. You need to show more code, then. Your MapRoute should include the area, either directly or indirectly. I don't see that in the code in the post. If you're using Steve Sanderson's code you need to realize that overriding defaults means you need to supply the area in your defaults.
Craig Stuntz
Code updated. If what you're saying is correct, I still don't see how it will match the route, I would expect it to default to the "Default" route, unless it inspects the controller and action against those specified in the route mapping.
roryf
see update. It would match the non-default route only if you took Garry's suggestion (in comments to his answer) of adding a default category in MapRoute.
Craig Stuntz
+1  A: 

Not ideal, but can you use the route name approach?

<%= Html.RouteLink("your link", "Category-List", new {category = "foo"})%>
Marc Gravell