MVC2 UrlParameter.Optional and dynamic breadcrumb links

Mar 24, 2010 at 3:01 PM

I am using Asp.net MVC2 with changeset 43259 of MvcSiteMap. I ran into a couple of issues when using the sitemap to generate breadcrumbs.

I have the following Web.sitemap:

    <?xml version="1.0" encoding="utf-8" ?>
    <siteMap>
      <mvcSiteMapNode title="Partners" controller="Partner" action="Index" >
        <mvcSiteMapNode title="Details" controller="Partner" action="Details" isDynamic="true" dynamicParameters="partnerId">
          <mvcSiteMapNode title="Create a new lookup" controller="PartnerLookup" action="Create" isDynamic="true" dynamicParameters="partnerId;partnerLookupId" />
          <mvcSiteMapNode title="Edit lookup" controller="PartnerLookup" action="Edit" isDynamic="true" dynamicParameters="partnerId;partnerLookupId" />
          <mvcSiteMapNode title="Delete lookup" controller="PartnerLookup" action="Delete" isDynamic="true" dynamicParameters="partnerId;partnerLookupId" />
        </mvcSiteMapNode>
      </mvcSiteMapNode>
    </siteMap>
And the following routes:
routes.MapRoute( _
    "PartnerLookup", _
    "Partner/{partnerId}/Lookup/{action}/{partnerLookupId}", _
    New With {.controller = "PartnerLookup", .partnerLookupId = UrlParameter.Optional}, _
    New With {.action = "(Create|Edit|Delete)"} _
)

routes.MapRoute( _
    "Partner", _
    "Partner/{action}/{partnerId}", _
    New With {.controller = "Partner", .action = "Details", .partnerId = UrlParameter.Optional}, _
    New With {.action = "(Create|Details|Edit|Delete)"} _
)

routes.MapRoute( _
    "DefaultIndex", _
    "{Controller}", _
    New With {.controller = "Partner", .action = "Index"}, _
    New With {.action = "(Index)"} _
)

I'm not sure that MvcSiteMap is handling the UrlParameter.Optional setting correctly and I think MvcSiteMapProvider.cs needs the following change:

    On line 744 change the following:
        if (pair.Value == null || string.IsNullOrEmpty(pair.Value.ToString()))
        {
            continue;
        }


     To:
        if (pair.Value == null || string.IsNullOrEmpty(pair.Value.ToString()) || pair.Value == UrlParameter.Optional)
        {
            continue;
        }

        
When I navigate to the "Create a new lookup" page of my site, the breadcrump link for it's parent "Details" loses the partnerId parameter even
though partnerId is in the current RouteValues. As a solution I have created the following attribute and added it to my base controller:

Imports System.Web.Mvc
    Imports System.Web
    Imports MvcSiteMap.Core
    Imports System.Web.Routing
    
    Namespace MVC
        Public Class SiteMapParentURLAttribute
            Inherits ActionFilterAttribute
    
            Public Sub New()
            End Sub
    
            Public Overrides Sub OnActionExecuting(ByVal filterContext As System.Web.Mvc.ActionExecutingContext)
    
                If SiteMap.CurrentNode IsNot Nothing AndAlso SiteMap.CurrentNode.ParentNode IsNot Nothing Then
                    Dim parent As MvcSiteMapNode = SiteMap.CurrentNode.ParentNode
    
                    parent.Url = UrlHelper.GenerateUrl(Nothing, parent.Action, parent.Controller, Nothing, Nothing, Nothing, New RouteValueDictionary(parent.RouteValues), RouteTable.Routes, filterContext.RequestContext, True)
    
                End If
    
            End Sub
    
        End Class
    End Namespace

Would it make sense to use the specified UrlHelper.GenerateUrl in MvcSiteMapNode.cs instead of the current UrlHelper.Action call in the Url property? These fixes might be specific to my needs but I would appreciate your input.

Thanks
Jeremy

Mar 24, 2010 at 3:22 PM

The SiteMapParentUrlAttribute I posted might not actually work - the following does work but it is probably more complex than it needs to be:

Imports System.Web.Mvc
Imports System.Web
Imports MvcSiteMap.Core
Imports System.Web.Routing

Namespace MVC
    Public Class SiteMapParentURLAttribute
        Inherits ActionFilterAttribute

        Public Sub New()
        End Sub

        Public Overrides Sub OnActionExecuting(ByVal filterContext As System.Web.Mvc.ActionExecutingContext)

            If SiteMap.CurrentNode IsNot Nothing AndAlso SiteMap.CurrentNode.ParentNode IsNot Nothing Then
                Dim parent As MvcSiteMapNode = SiteMap.CurrentNode.ParentNode

                Dim urlHelper = New UrlHelper(filterContext.RequestContext)

                Dim mergedRouteValues = parent.RouteValues
                Dim currentRouteValues = filterContext.RouteData

                For Each key As String In parent.DynamicParameters

                    If currentRouteValues.Values.ContainsKey(key) Then

                        If Not mergedRouteValues.ContainsKey(key) Then
                            mergedRouteValues.Add(key, currentRouteValues.Values(key))
                        End If

                    End If

                Next

                parent.Url = urlHelper.Action(parent.Action, parent.Controller, New RouteValueDictionary(mergedRouteValues))
                'parent.Url = UrlHelper.GenerateUrl(Nothing, parent.Action, parent.Controller, Nothing, Nothing, Nothing, New RouteValueDictionary(parent.RouteValues), RouteTable.Routes, filterContext.RequestContext, True)
            End If

        End Sub

    End Class
End Namespace

 

Mar 30, 2010 at 10:39 AM

I will add this as DynamicParentNodeAttribute