Pregunta

Tenemos un sitio web que utiliza un mapa de sitio predeterminado estándar de bog con recorte de seguridad de la siguiente manera:

<siteMap defaultProvider="default" enabled="true">
  <providers>
    <add siteMapFile="~/Web.sitemap" securityTrimmingEnabled="true" name="default" type="System.Web.XmlSiteMapProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  </providers>
</siteMap>

Todo muy bien, pero se ha recibido una solicitud para cambiar el Título de un nodo en función de algunos criterios de fondo. Suena como algo simple, pero aparentemente no.

Intento 1 : manejo del evento SiteMapResolve . No parece importar dónde se maneje este evento, lo he mostrado en Global.asax simplemente porque ese fue uno de los lugares donde lo probé y funcionó.

Public Class Global_asax
    Inherits System.Web.HttpApplication

    Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
        AddHandler SiteMap.SiteMapResolve, AddressOf SiteMapResolve
    End Sub

    Sub Application_EndRequest(ByVal sender As Object, ByVal e As EventArgs)
        RemoveHandler SiteMap.SiteMapResolve, AddressOf SiteMapResolve
    End Sub

    Private Shared Function SiteMapResolve(ByVal sender As Object, ByVal e As SiteMapResolveEventArgs) As SiteMapNode

        Dim node As SiteMapNode = SiteMap.CurrentNode
        If IsThisTheNodeToChange(node) Then
            node = node.Clone()
            node.Title = GetNodeTitle()
        End If
        Return node

    End Function

End Class

Esto funcionó bien cuando se navegó por la página relevante, pero desafortunadamente parte de la navegación del sitio involucra un cuadro combinado que está vinculado a los datos del mapa del sitio de esta manera:

<asp:SiteMapDataSource ID="siteMapDataSource" runat="Server" ShowStartingNode="false" StartFromCurrentNode="false" StartingNodeOffset="1" />
<asp:DropDownList ID="pageMenu" runat="Server" AutoPostBack="True" DataSourceID="siteMapDataSource" DataTextField="Title" DataValueField="Url" />

Cuando se representa este menú, el evento SiteMapResolve no activa ninguno de los contenidos porque el nodo actual es la página en la que se define el menú. Como resultado, el menú muestra el título de marcador de posición sin sentido del archivo del mapa del sitio físico en lugar del título correcto.

Intento 2 : escribir mi propio proveedor de mapa del sitio. No quería duplicar todo el comportamiento predeterminado, así que intenté derivar del proveedor predeterminado de la siguiente manera.

Public Class DynamicXmlSiteMapProvider
    Inherits XmlSiteMapProvider

    Private _dataFixedUp As Boolean = False

    Public Overrides Function GetChildNodes(ByVal node As SiteMapNode) As SiteMapNodeCollection

        Dim result As SiteMapNodeCollection = MyBase.GetChildNodes(node)
        If Not _dataFixedUp Then
            For Each childNode As SiteMapNode In result
                FixUpNode(childNode)
            Next
        End If
        Return result

    End Function

    Private Sub FixUpNode(ByVal node As SiteMapNode)

        If IsThisTheNodeToChange(node) Then
            node.ReadOnly = False
            node.Title = GetNodeTitle()
            node.ReadOnly = True
            _dataFixedUp = True
        End If

    End Sub

End Class

Esto no funciona porque GetChildNodes no parece llamarse muy a menudo cuando se navega por el sitio.

Intento 3 : intente corregir los datos inmediatamente después de cargarlos en la memoria, en lugar de cuando se accede a ellos.

Public Class DynamicXmlSiteMapProvider
    Inherits XmlSiteMapProvider

    Private _dataFixInProgress As Boolean = False
    Private _dataFixDone As Boolean = False

    Public Overrides Function BuildSiteMap() As SiteMapNode

        Dim result As SiteMapNode = MyBase.BuildSiteMap()
        If Not _dataFixInProgress AndAlso Not _dataFixDone Then
            _dataFixInProgress = True
            For Each childNode As SiteMapNode In result.GetAllNodes()
                FixUpNode(childNode)
            Next
            _dataFixInProgress = False
            _dataFixDone = True
        End If
        Return result

    End Function

    Private Sub FixUpNode(ByVal node As SiteMapNode)

        If IsThisTheNodeToChange(node) Then
            node.ReadOnly = False
            node.Title = GetNodeTitle()
            node.ReadOnly = True
        End If

    End Sub

End Class

Esto parece funcionar. Sin embargo, me preocupa la llamada a GetAllNodes en el método BuildSiteMap . Me parece incorrecto extraer de forma recursiva todos los datos en la memoria solo para fijar un valor. Además, no tengo control sobre cuándo se llama a BuildSiteMap . Preferiría algo más como el Intento 1, que se llama a pedido cuando se requieren los datos del nodo por primera vez.

Intento 4 (NUEVO) : igual que el Intento 2, pero anulando todos los miembros virtuales que tienen que ver con la lectura de datos ( CurrentNode , < code> FindSiteMapNode , FindSiteMapNodeFromKey , GetChildNodes , GetCurrentNodeAndHintAncestorNodes , GetCurrentNodeAndHintNeighborhoodNodes , GetParentNode GetParentNode < code>, GetParentNodeRelativeToCurrentNodeAndHintDownFromParent , GetParentNodeRelativeToNodeAndHintDownFromParent , HintAncestorNodes , HintNeighborhoodNodes , para intentar la lectura del nodo dinámico en algún lugar.

Esto no funcionó. Pongo declaraciones de depuración en todos los miembros anulados, y parece que ninguno de ellos se llama cuando se enlazan datos a la lista desplegable. La única explicación que se me ocurre es que todos los nodos se leen en la memoria de una sola vez durante la llamada BuildSiteMap , para que el SiteMapNode no llegue a la clase del proveedor al enumerar nodos secundarios.

¿Alguien tiene alguna sugerencia mejor?

¿Fue útil?

Solución

En nuestro SiteMapProvider personalizado, anulamos el Método BuildSiteMap y construimos los SiteMapNodes manualmente. Para cambiar y / o agregar propiedades personalizadas, agregamos atributos personalizados a los SiteMapNodes creando una NameValueCollection y agregamos pasar esto al SiteMapNode Constructor.

Otros consejos

Estás bastante cerca del intento n. ° 2: solo necesitas anular GetParentNode y FindSiteMapNode también.

Gracias a Mark y Rex por las sugerencias. Lo que terminé haciendo fue dejar solo al proveedor del mapa del sitio y simplemente arreglar un nodo en la página maestra, por lo tanto:

Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init

    ' Do this as early as possible in the page lifecycle so that it happens before any
    ' automatic data binding or control initialisation is done.
    Dim node As SiteMapNode = GetNodeToEdit()
    Dim nodeReadOnly As Boolean = node.ReadOnly
    node.ReadOnly = False
    node.Title = GetNodeTitle()
    node.ReadOnly = nodeReadOnly

End Sub

Sin embargo, he aceptado la respuesta de Mark, porque así es como lo haré si resulta que es necesario realizar modificaciones más extensas en el futuro.

Gracias Christian por tu investigación. Utilizando sus resultados, se me ocurrió lo siguiente que puede ayudar a otros:

' Dynamically update some menu items.
'
' Since the correct Medical Center ID is not known until runtime, need to
' append "&MEDICAL_CENTER_ID=xxx" to all of the report's URLs in Web.sitemap.
Public Shared Sub UpdateMenu(ByVal MedicalCenterID As String)
  Dim CurrentNodeTitle As String = ""
  Dim NodeReadOnlyProperty As Boolean

  ' Home menu item
  CurrentNodeTitle = SiteMap.CurrentNode.Title

  ' Determines if the current node has child nodes.
  If (SiteMap.CurrentNode.HasChildNodes) Then
    ' Loop through top level 1 menu items (looking for Reports)
    For Each ChildNodesEnumerator1 As SiteMapNode In SiteMap.CurrentNode.ChildNodes
      CurrentNodeTitle = ChildNodesEnumerator1.Title
      If CurrentNodeTitle = "Reports" Then
        ' Loop through level 2 menu items (looking for specfic reports)
        For Each ChildNodesEnumerator2 As SiteMapNode In ChildNodesEnumerator1.ChildNodes
          CurrentNodeTitle = ChildNodesEnumerator2.Title
          If CurrentNodeTitle = "Multi-Day Vehicle Requests" Or _
           CurrentNodeTitle = "XXXXXXXXXXXXXXXXX" Then
            ' First check if the URL has not been modified already
            If InStr(ChildNodesEnumerator2.Url, "MEDICAL_CENTER_ID") = 0 Then
              NodeReadOnlyProperty = ChildNodesEnumerator2.ReadOnly
              ChildNodesEnumerator2.ReadOnly = False
              ChildNodesEnumerator2.Url = ChildNodesEnumerator2.Url & "&MEDICAL_CENTER_ID=" & MedicalCenterID
              ChildNodesEnumerator2.ReadOnly = NodeReadOnlyProperty
            End If
          End If
        Next
      End If
    Next
  End If
End Sub
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top