tags:

views:

732

answers:

4

Hi all,

I have a route

// Sample URL: /Fixtures/Team/id
routes.MapRoute(
     "Fixtures-by-TeamID",
     "Fixtures/Team/{teamId}",
     new { controller = "Fixtures", action = "GetByTeamID", },
     new { teamId = @"\d{1,3}" }
);

and I am trying to use ActionLink in ASP.net MVC p5.

<%= Html.ActionLink(g.HomeTeam.TeamName, "Team", new { teamId = g.HomeTeam.TeamID })%>

However it is not working and giving me

<a href="/Fixtures/Team?teamId=118">Team A</a>

If I use Url.RouteUrl i get the correct link.

<a href="<%=Url.RouteUrl("Fixtures-by-TeamID", new { teamId = g.HomeTeam.TeamID })%>"><%=g.HomeTeam.TeamName%></a>   

<a href="/Fixtures/Team/118">Team A</a>

Any help would be great? Will this change in ASP.net MVC beta?

Thanks

Donald

+1  A: 

In my experience, the only time action routes really work properly is when you only have the single default route. As soon as you start adding custom routes like yours above, action routes become... I guess "finicky" is the right word. The matching system doesn't work exactly how you'd expect it to.

In this case, you said the action was "Team", but this route doesn't match an action of "Team", it only matches "GetTeamByID". So the routing system keeps going and ends up giving you a route based on the default route. The teamId isn't explicitly part of your default route, so it ends up as a query parameter tacked on the end.

MVC Beta has already shipped, and this behavior is unchanged.

Also, don't you find the named route to be clearer anyway? Personally, I do.

I even go one step further and actually create route helpers for all my custom routes, which might look like this in your current example:

<a href="<%= Url.FixturesByTeam(g.HomeTeam.TeamID) %>"><%= g.HomeTeam.TeamName %></a>

Or even:

<%= Html.LinkToFixturesByTeam(g.HomeTeam) %>

where you can pull the values for name and ID directly from the model.

Brad Wilson
+1  A: 

Try this:

// Sample URL: /Fixtures/Team/id
routes.MapRoute(
     "Fixtures-by-TeamID",
     "Fixtures/Team/{teamId}",
     new { controller = "Fixtures", action = "Team", teamId = -1 }
);

your controller should look like:

public class FixturesController : BaseController // or whatever 
{
  /*...*/
  public ActionResult Team(int teamId)
  {
     return View("Detail", Team.GetTeamById(teamId)) // or whatever
  }
  /*...*/
}

And your link would look like

<%= Html.ActionLink("Click here for the team details", "Team", "Fixtures", new { teamId = ViewModel.Data.Id /*orwhateverlol*/ }) %>

(I don't have MVC on this machine so this is all from memory; may have a syntax error or some arguments reversed).

Note your route map's path matches your 1)controller, 2) action 3) argument name. I've found the default action (third argument in MapRoute) works, whereas your overload of that method I've never seen before (may be a holdover from a previous release).

Also observe how your FixturesController matches the path (Fixtures) and the action name matches (Team), and the argument matches as well (teamId).

Lastly, your ActionLink's last argument must match your controller's arguments in name (teamId) and type.

Its a bit too "magical" at this point (there's LOTS of string comparisons going on in the background!). Hopefully this will improve over time. The old Expression style was MUCH MUCH better. You essentially called the method you wished to run, with the values you wished to pass it. I hope they bring that expression style back into the framework. Haack?

Will
The "Expression Style" resulted in the same magical strings behind the scenes, with the same associated matching issues. Whether it's "much better" is a matter of taste; personally, I find that simulating the call of an action method just to get its name is horrifying.
Brad Wilson
A: 

When a parameter ("action" in this case) is defined only in defaults and not in the route url, it has to be an exact match (unless you force it to go against a particular route as in the RouteUrl case).

To make everything work as is right now, you could add another route to the list just below the above route:

routes.MapRoute( "Fixtures-by-TeamID1", "Fixtures/Team/{teamId}", new { controller = "Fixtures", action = "Team", }, new { teamId = @"\d{1,3}" } );

OR you could add the action parameter to the route url,

OR you could use the named route as you did.

+1  A: 

Have you tried this yet?

Html.ActionLink<FixturesController>(c => c.GetByTeamID(g.HomeTeam.TeamID), "Team")

Also

You might want to add action = "GetByTeamID" to your constraints.

Rob