1

I am having the following problem. I setup MVC SiteMap but there is a node which I need to save (preserve) the parameter. Technically the problem explained:

I have route: Agent/Checklists/Templates, from there I am opening specific template, Agent/Checklists/EditTemplate/1 (where 1 is the id) Then from there I am opening new page which is Agent/Processes/Add

In the last page I have the breadcrumb: Templates > Edit Template > Add Process

And now I want when I click on Edit Template to get me redirected to Agent/Checklists/EditTemplate/1

I tried putting SiteMapPreserveRouteData decorator on the Action but it says it is obsolete.

Here is my Mvc.sitemap

<mvcSiteMapNode title="Templates" controller="Checklists" action="Templates" area ="Agent">
        <mvcSiteMapNode title="Edit Template" controller="Checklists" action="EditTemplate" area="Agent" preservedRouteParameters="id">
          <mvcSiteMapNode title="Add Process" controller="Processes" action="Add" area="Agent" preservedRouteParameters="id, id" />
          <mvcSiteMapNode title="Process Configuration" controller="Processes" action="Edit" area="Agent" preservedRouteParameters="token" />
        </mvcSiteMapNode>
      </mvcSiteMapNode>

Thanks

Martin Djonov
  • 43
  • 2
  • 7

1 Answers1

1

Preserved route parameters are preserved from the current request. Therefore, each route key must be unique within the same node ancestry. In other words, when using preservedRouteParameters you cannot reuse id again for a different purpose (different entity).

Using preservedRouteParameters, the id route value will only work for one level. If you nest levels deeper than that, you will need to create a unique route key for each level.

And you do need to ensure you preserve the parameters of the parent node for every ancestor, or the URL won't be constructed correctly.

<mvcSiteMapNode title="Templates" controller="Checklists" action="Templates" area="Agent">
    <mvcSiteMapNode title="Edit Template" controller="Checklists" action="EditTemplate" area="Agent" preservedRouteParameters="checklistId">
        <mvcSiteMapNode title="Add Process" controller="Processes" action="Add" area="Agent" preservedRouteParameters="processId,checklistId" />
        <mvcSiteMapNode title="Process Configuration" controller="Processes" action="Edit" area="Agent" preservedRouteParameters="token,processId,checklistId" />
    </mvcSiteMapNode>
</mvcSiteMapNode>

A simple way to manage this is to add a route per controller.

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "Checklists",
            "Checklists/{action}/{checklistId}",
            new { controller = "Checklists", action = "Index", checklistId = UrlParameter.Optional });

        routes.MapRoute(
           "Processes",
           "Processes/{action}/{processId}",
           new { controller = "Processes", action = "Index", processId = UrlParameter.Optional });

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

Then you need to add the ancestry data to each URL.

@Html.ActionLink("Edit Process", "Edit", "Process", new { token = "1234", processId = "5678", checklistId = "23" } , null)

See these demos for samples of using both preservedRouteParameters and dynamic node providers.

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • After some reading of the documentation I understood how preservedRouteParameters work. But we have that kind of system where parent has one id and the child node has another id. Then it came to my mind to save custom attributes and override the SiteMapNodeModel.cshtml to construct the url correctly but no luck, it remembers the attributes for the current request but when I go down in the child, the attributes for the parent are lost. I must admit I am not happy with the functionalities available in this feature because all I need is a stack with exactly how the routes were called. – Martin Djonov Mar 05 '17 at 11:36
  • There is nothing stopping you from ditching preservedRouteParameters and just adding a node for every value (the default behavior), which is why I mentioned dynamic node providers. preservedRouteParamters only works under specific conditions - one of those being that the entire ancestry must not duplicate the same route key. It scales better if you are doing CRUD operations, but has a lot less flexibility. – NightOwl888 Mar 05 '17 at 11:56
  • Hmm, the business situation is the following: I am opening some template with some id, then from there I am opening a process with some token (different than the template token). Also I am having the following situation: Person File > Employment > Job > Terminate Job where all of them have different id parameter and I don't see any possible option to solve this. So I am looking at your dynamic example but this solves me only one level I guess. And I have a lot of places in the system where I would have to achieve this so it's kind of getting static writing.. – Martin Djonov Mar 05 '17 at 12:15
  • You can use a single dynamic node provider to nest multiple levels, quite easily. Or you can use a dynamic node provider per table - generally, you just put your primary/foreign keys right into the key/parent key (perhaps with a table prefix). – NightOwl888 Mar 05 '17 at 13:54
  • But then, for this scenario you could go with preservedRouteParameters - you just need to add a route for each controller so you can rename the "id" key `url: "Checklists\{action}\{checklistId}" (and set the default `controller="Checklists"`). – NightOwl888 Mar 05 '17 at 13:55