I'm new to MVCSiteMap and I have a simple question:
I use the default route config like this:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
Now in my controller, I want to create and edit an entity in the same Action:
public ActionResult AddEdit(int? id)
{}
so if the id is null, it means add, and if it is not null then the action is edit.
Now I want the site map to realize the different from add and edit. I tried this:
<mvcSiteMapNode title="Parent" controller="Class" action="Index">
<mvcSiteMapNode title="Add" controller="Class" action="AddEdit" />
<mvcSiteMapNode title="Edit" controller="Class" action="AddEdit" inheritedRouteParameters="Id"/>
</mvcSiteMapNode>
but seems it does not work well. It always use the second one.
What should I do?
Thanks a lot.
There are 2 options.
Option 1
Create a single node that sets preservedRouteParameters="id" on each of the nodes that correspond to an action method with a parameter. This creates a 1-to-1 relationship between the nodes and action methods, but a 1-to-many relationship between the node and the actual entities.
<mvcSiteMapNode title="Products" controller="Product" action="Index">
<mvcSiteMapNode title="Create New" controller="Product" action="Create" visibility="SiteMapPathHelper,!*" />
<mvcSiteMapNode title="Details" controller="Product" action="Details" visibility="SiteMapPathHelper,!*" preservedRouteParameters="id">
<mvcSiteMapNode title="Edit" controller="Product" action="Edit" visibility="SiteMapPathHelper,!*" key="Product_Edit" preservedRouteParameters="id"/>
<mvcSiteMapNode title="Delete" controller="Product" action="Delete" visibility="SiteMapPathHelper,!*" preservedRouteParameters="id"/>
</mvcSiteMapNode>
</mvcSiteMapNode>
This is the recommended way to do it if you are creating pages that edit data, especially if those pages will never be indexed by search engines.
In most cases, you will also need to setup the FilteredSiteMapNodeVisibilityProvider and SiteMapTitleAttribute to fix the visibility and title of the nodes. You won't be able to use this method for anything other than a breadcrumb trail, so it is important to hide these fake nodes from the other HTML helpers like the Menu and SiteMap.
For a complete demo of how this can be done, visit How to Make MvcSiteMapProvider Remember a User's Position.
Option 2
Use a custom IDynamicNodeProvider to create a node per entity (1-to-1 relationship).
public class StoreDetailsDynamicNodeProvider
: DynamicNodeProviderBase
{
public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
{
using (var storeDB = new MusicStoreEntities())
{
// Create a node for each album
foreach (var album in storeDB.Albums.Include("Genre"))
{
DynamicNode dynamicNode = new DynamicNode();
dynamicNode.Title = album.Title;
dynamicNode.ParentKey = "Genre_" + album.Genre.Name;
dynamicNode.RouteValues.Add("id", album.AlbumId);
yield return dynamicNode;
}
}
}
}
To use this, you need to ensure you set up your key and parent keys in code so each node understands what parent node it belongs to. You may need to explicitly set the "key" attribute in your XML in order to do this. You also need to ensure you set the "id" routeValue on each record to ensure your node matches your incoming route.
Use this method when your pages must be indexed by search engines and/or you want to see the nodes in the menu.
Do note that you can combine these 2 options in the same application and it will work fine. Both of these methods will also work for any number of custom route values (other than "id") as well.