I am using MvcSiteMapProvider
to generate breadcrumbs and I am having trouble matching nodes with a new feature. We use MVC5 areas and are using the latest MvcSiteMapProvider.MVC5
libraries. We are using i18n
with Resx
files, our title
attribute being keys. Our page URLs don't change after release, so use the standard XML config.
We use MVC5 attribute based routing.
The List
action is the default action for both the Home controller and the area, so is on the Store/
route. It works fine, the match is made.
The Search
action Store/Search
route does not match a node.
Configuration
<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0"
xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0 MvcSiteMapSchema.xsd">
<mvcSiteMapNode controller="Dashboard" action="Index" title="Foobar" key="Bar">
<!-- quite a large file -->
<mvcSiteMapNode area="Store" controller="Home" action="List" title="SiteMap_DocumentStore_Home_List" preservedRouteParameters="page, itemsPerPage, msg">
<mvcSiteMapNode area="Store" controller="Home" action="Search" title="SiteMap_DocumentStore_Search" preservedRouteParameters="tags, page"/>
<!-- snip extra entries -->
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMap>
I appreciate that I can remove attributes area
and controller
from the mvcSiteMapNode
children of List
. I've left them in here for completeness.
Home Controller
[RouteArea("Store")]
[Route("{action=list}")]
public class HomeController : Controller
{
[Route("{page?}/{itemsPerPage?}")]
public ActionResult List(int page = 1, int itemsPerPage = -1, string msg = "")
{}
[Route("Search/{tags?}/{page?}")]
public ActionResult Search(string tags = "", int page = 1)
{}
}
Investigation
I have a feeling that it is something to do with the MVC Route for the List
action being empty. If I change the route of List
to:
[Route("List/{page?}/{itemsPerPage?}")]
public ActionResult List(int page = 1, int itemsPerPage = -1, string msg = "")
{}
Then the Search node will then match, as will its siblings (that I snipped out)
Edit - Simplify the routing
I have removed the default route for the controller [Route("{action=list}")]
. The problem still persists.
Per MSDN:
Default Route
You can also apply the [Route] attribute on the controller level, capturing the action as a parameter. That route would then be applied on all actions in the controller, unless a specific [Route] has been defined on a specific action, overriding the default set on the controller.
In your case, the default controller-level route will be completely ignored because in each case you have a route at the action level that is overriding it.
I looked into why it is "not matching" by starting a new MVC 5 project in VS 2015 and adding an area and the rest of your config. For awhile I was perplexed as to why it wasn't working.
Then I discovered that the scaffolding wires up a different layout page for each Area in /Area/<area name>/Views/_ViewStart.cshtml
.
@{
Layout = "~/Areas/Store/Views/Shared/_Layout.cshtml";
}
I changed it to use the shared ViewStart.cshtml
file, and then it showed the breadcrumb trail.
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
Also, you have an issue with your preserved route parameters. Since they are always derived from the current request, the inner request must always provide all of the parameters of its ancestors. Furthermore, the parameters must not have a different meaning between parent and child, so for example page
must refer to the same page for both List
and Search
. In other words, each key name must be unique within its ancestry.
If they are the same, you can fix this by simply adding the additional parameter to the Search URL.
[Route("Search/{page?}/{itemsPerPage?}/{tags?}")]
Otherwise you should give each page
parameter a different name.
See How to make MvcSiteMapProvider Remember a User's Position and the demos included for guidance.