MvcSiteMapProvider 2.0: node for action with parameters - problem 2

Jun 22, 2010 at 9:50 PM

hello,

earlier I wrote about issue with node for action with 1 parameter: http://mvcsitemap.codeplex.com/Thread/View.aspx?ThreadId=216801 (it was fixed in changeset 47617). I found another issue - when there are several parameters in actions, MvcSiteMapNode doesn't contains these parameters - i.e. its URL is equal to /Controller/Action instead of /Controller/Action?ParamKey1=Val1&ParamKey2=Val2.

Let me explain on example. Suppose that we have the following action:

 

public class ProducController : Controller
{
    ...
    public ActionResult List(int? Category, int? page)
    {
        ...
    }
}

 

This action corresponds for example to the following URL: http://example.com/Product/List?Category=2&page=1 and it shows 1st page of list of products from CategoryId = 2. And I need the following SiteMapPath when some product is selected from this list:

Root > Selected Category > Selected Product

I defined the following site map:

<mvcSiteMap xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-2.0">
  <mvcSiteMapNode title="$Resources:Breadcrumb,Home_Index" controller="Home" action="Index">
    ...
    <mvcSiteMapNode title="$Resources:Breadcrumb,Product_List" controller="Product" action="List">
      <mvcSiteMapNode title="$Resources:Breadcrumb,Product_View" controller="Product" action="View" />
    </mvcSiteMapNode>
   </mvcSiteMapNode>
</mvcSiteMapNode>

The problem is that [Selected Category] node (which corresponds to Product/List action in site map) doesn't contains query string (?Category=2&page=1). It only contains basic URL http://example.com/Product/List. But I expect that last selected Category and page params will be included into URL (it is important that exactly last selected Category and page params should be included into URL - because when user selected another category - he should see this category in [Selected Category] site map node).

I investigated that many people also encountered with similar problems, for example:

But I didn't found suitable solution in these topics. So is there a way to remember query string parameters in MvcSiteMapNode ?

PS. Technical info which may help:

  • ASP.Net MVC 2.0.0.0
  • MvcSiteMapProvider 2.0.0.0
  • No areas
  • Default route is used:

 

routes.MapRoute(
    "Default", 
    "{controller}/{action}/{id}",  parameters
    new { controller = "Home", action = "Index", id = "" } 

 

Jun 24, 2010 at 4:49 PM

I found the reason of this behavior. Seems like currently MvcSiteMapProvider doesn't works with query string (?Category=1&page=1). It works only with route data (/Category/1/page/1). See DefaultSiteMapProvider.GetSiteMapNodeFromXmlElement() method which sets route values into MvcSiteMapNode instance. So I made a workaround for this issue instead of fixing it: I added new route so Category is specified in URL path instead of query string:

 

routes.MapRoute("Product_List_route", "Product/List/Category/{Category}",
    new { controller = "Product", action = "List", Category = "" });

After this [Selected Category] node gets /Category/2 route value (I was also needed to add Category param into Product List action - in order to provide rout value for the [Selected Category] node. But it is quite logical since 1 product can belong to several categories. And node should know in context of what category this product is now opened).

So this workaround worked for me. But is such behaviour is really by design (i.e. that MvcSiteMapProvider ignores query string parameters)?

Coordinator
Jun 30, 2010 at 12:49 PM

This is indeed by design, there is no easy way to fix this in MvcSiteMapProvider.

A possible valid way to fix this is to do the following: in your Product/View action method, set the 

SiteMap.CurrentNode.ParentNode.Url

to the correct URL including query parameters.

Apr 4, 2013 at 8:36 PM
I know this thread is a bit old, but I came across the same issue. Why wouldn't you create a new attribute type that sets parameters in OnActionExecuting?
public class SiteMapPreserveParametersAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var node = SiteMap.CurrentNode as MvcSiteMapNode;

        if (node != null)
        {
            foreach (var parameter in filterContext.ActionParameters)
            {
                node.RouteValues[parameter.Key] = parameter.Value;
            }
        }   
    }
}
Apr 5, 2013 at 5:46 AM
hi
it also can be used, but will require to add this attribute to all controllers as far as I understand. If you have single base class for controllers, it is not big problem though.