Question

I'm currently using the MvcSiteMapProvider to generate my breadcrumbs for me dynamically by

@Html.MvcSiteMap().SiteMapPath()

My website is all branded, and the first value in the Url contains the brand, so my routes all look like this:

routes.MapRoute("Terms", "{brand}/Terms", new { controller = "Legal", Action = "Terms" });
routes.MapRoute("Privacy", "{brand}/Privacy", new { controller = "Legal", Action = "Privacy" });
routes.MapRoute("Home", "{brand}", new { controller = "Home", action = "Dashboard" });
routes.MapRoute("Dashboard", "{brand}/Dashboard", new { controller = "Home", action = "Dashboard" });

This was working in a previous version somewhat, but I saw intermittent issues with viewing the same route with two different brands. Once I updated, I get nothing at all now.

My sitemap looks like this:

  <mvcSiteMapNode title="Home" url="/{brand}" route="Home">
    <mvcSiteMapNode title="Dashboard" url="/{brand}/Dashboard" route="Dashboard" />
    <mvcSiteMapNode title="Terms" url="/{brand}/Terms" route="Terms" />
    <mvcSiteMapNode title="Privacy" url="/{brand}/Privacy" route="Privacy" />
  </mvcSiteMapNode>

I've dug through documentation for updating, and tried a ton of different things. I've included the source in my project and debugged, and the only thing I did see was that no matter what I do, currentNode is always null.

Any ideas?

Was it helpful?

Solution

The url attribute/property is for configuring URLs with absolute paths, not for configuring routes. When you use the url attribute, it basically disables routing support on the node and turns it into a URL-based node. This comes in handy with interoperability with ASP.NET or providing external links in your SiteMap. Your Urls don't match because they are not real URLs, which is what is expected.

To use MVC routing on the nodes, you basically have to configure them the way you would configure an ActionLink or RouteLink (when used with the optional route attribute).

<mvcSiteMapNode title="Home" controller="Home" action="Dashboard" route="Home">
    <mvcSiteMapNode title="Dashboard" action="Dashboard" route="Dashboard" />
    <mvcSiteMapNode title="Terms" controller="Legal" action="Terms" route="Terms" />
    <mvcSiteMapNode title="Privacy" controller="Legal" action="Privacy" route="Privacy" />
</mvcSiteMapNode>

Note also that when configuring using XML, the area and controller properties are inherited automatically from the ancestor node that last defined them, which can save you some grunt work. And again, the routes are totally optional.

<mvcSiteMapNode title="Home" controller="Home" action="Dashboard">
    <mvcSiteMapNode title="Dashboard" action="Dashboard" />
    <mvcSiteMapNode title="Terms" controller="Legal" action="Terms" />
    <mvcSiteMapNode title="Privacy" controller="Legal" action="Privacy" />
</mvcSiteMapNode>

But using custom route parameters is where it gets tricky. Have a look at this answer for an explanation of how that can be done.

Update

I took another look at your configuration and I think I have spotted the problem. You don't have your nodes configured to take the brand parameter into consideration, therefore they will never match. If (as I suspect) you are trying to make all of the brands match this single set of nodes, you need to add "brand" as a preservedRouteParameter force it to match.

<mvcSiteMapNode title="Home" controller="Home" action="Dashboard" preservedRouteParameters="brand">
    <mvcSiteMapNode title="Dashboard" action="Dashboard" preservedRouteParameters="brand"/>
    <mvcSiteMapNode title="Terms" controller="Legal" action="Terms" preservedRouteParameters="brand"/>
    <mvcSiteMapNode title="Privacy" controller="Legal" action="Privacy" preservedRouteParameters="brand"/>
</mvcSiteMapNode>

This causes the brand of the current request to be copied into the node's RouteValues before comparing them. To get a match, you need to have the same keys and values (values are case insensitive) in the node's RouteValues as there are in the RouteValues of the current request. If you don't, the currentNode will always be null.

The reason why it may have seemed to work before was because v4.4.x was also matching on URL, which was a bug that caused invalid matches to happen.

There is a complete post that goes into detail about how the matching works titled How to Make MvcSiteMapProvider Remember a User's Position.

Update 2

I also noticed that your defaults are not set up right on your routes. "Action" should be "action" as the route keys are case sensitive.

// This is what you have
routes.MapRoute("Terms", "{brand}/Terms", new { controller = "Legal", Action = "Terms" });
routes.MapRoute("Privacy", "{brand}/Privacy", new { controller = "Legal", Action = "Privacy" });

// Action should always be lowercase (action)
routes.MapRoute("Terms", "{brand}/Terms", new { controller = "Legal", action = "Terms" });
routes.MapRoute("Privacy", "{brand}/Privacy", new { controller = "Legal", action = "Privacy" });
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top