Domanda

Abbiamo un sito Web che utilizza una sitemap predefinita standard di tipo bog con limiti di sicurezza come segue:

<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>

Tutto molto bene, ma è arrivata una richiesta per cambiare il Title di un nodo in base ad alcuni criteri di back-end. Sembra una cosa semplice, ma apparentemente no.

Tentativo 1 : gestione dell'evento SiteMapResolve . Non sembra importare dove questo evento viene gestito, l'ho mostrato in Global.asax semplicemente perché quello era uno dei luoghi in cui l'ho provato e ha funzionato.

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

Funzionava perfettamente quando si navigava nella pagina pertinente, ma sfortunatamente parte della navigazione del sito comporta una casella combinata che è legata ai dati della mappa del sito in questo modo:

<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" />

Quando viene visualizzato questo menu, l'evento SiteMapResolve non viene attivato per nessuno dei contenuti perché il nodo corrente è la pagina in cui è definito il menu. Di conseguenza, il menu mostra il titolo segnaposto senza senso dal file della Sitemap fisica anziché il titolo corretto.

Tentativo 2 - Scrittura del mio provider di sitemap. Non volevo duplicare tutto il comportamento predefinito, quindi ho provato a derivare dal provider predefinito come segue.

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

Questo non funziona perché GetChildNodes non sembra essere chiamato molto spesso durante la navigazione nel sito.

Tentativo 3 : prova a correggere i dati immediatamente dopo averli caricati in memoria, anziché quando vi si accede.

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

Questo sembra funzionare. Tuttavia, sono preoccupato per la chiamata a GetAllNodes nel metodo BuildSiteMap . Mi sembra sbagliato estrarre ricorsivamente tutti i dati in memoria solo per correggere un valore. Inoltre, non ho alcun controllo su quando viene chiamato BuildSiteMap . Preferirei qualcosa di più simile al Tentativo 1, che viene chiamato su richiesta quando i dati del nodo sono richiesti per la prima volta.

Tentativo 4 (NUOVO) - Come il Tentativo 2, ma sovrascrivendo tutti i membri virtuali che hanno a che fare con la lettura dei dati ( CurrentNode , < code> FindSiteMapNode , FindSiteMapNodeFromKey , GetChildNodes , GetCurrentNodeAndHintAncestorNodes , GetCurrentNodeAndHintNepartoNodes , GetParentNode , GetParentNodeRelativeToCurrentNodeAndHintDownFromParent , GetParentNodeRelativeToNodeAndHintDownFromParent , HintAncestorNodes , HintNecome theode to theode to thecept to the code to) da qualche parte.

Questo non ha funzionato. Ho inserito le dichiarazioni di debug in tutti i membri sovrascritti e sembra che nessuno di loro venga chiamato quando i dati si collegano all'elenco a discesa. L'unica spiegazione a cui riesco a pensare è che i nodi vengono letti tutti in memoria in una volta sola durante la chiamata BuildSiteMap , in modo che SiteMapNode non colpisca la classe del provider durante l'enumerazione nodi figlio.

Qualcuno ha qualche suggerimento migliore?

È stato utile?

Soluzione

Nel nostro SiteMapProvider personalizzato sovrascriviamo il metodo BuildSiteMap e costruiamo manualmente i SiteMapNodes. Per modificare e / o aggiungere proprietà personalizzate, aggiungiamo attributi personalizzati a SiteMapNodes creando un NameValueCollection e aggiungendolo al costruttore SiteMapNode.

Altri suggerimenti

Sei abbastanza vicino con il tentativo n. 2: devi solo sostituire GetParentNode e TrovaSiteMapNode .

Grazie a Mark e Rex per i suggerimenti. Quello che ho finito per fare è stato quello di lasciare da solo il fornitore della sitemap e di sistemare l'unico nodo nella pagina principale, quindi:

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

Tuttavia, ho accettato la risposta di Mark, perché è così che lo farò se si scopre che in futuro dovranno essere apportate modifiche più estese.

Grazie Christian per la tua ricerca. Utilizzando i tuoi risultati, ho ideato quanto segue che può aiutare gli altri:

' 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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top