我们有一个网站,它使用沼泽标准的默认站点地图,并进行了如下安全调整:
<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>
一切都很好,但是收到了一个请求,要求根据某些后端标准更改一个节点的 Title
。听起来像是一件简单的事情,但显然并非如此。
尝试 1 - 处理 SiteMapResolve
事件。这个事件在哪里处理似乎并不重要,我在 Global.asax 中显示它只是因为那是我尝试过并且有效的地方之一。
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
当导航到相关页面时,这种方法工作得很好,但不幸的是,站点导航的一部分涉及到数据绑定到站点地图的组合框,如下所示:
<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" />
呈现此菜单时,SiteMapResolve
事件不会触发任何内容,因为当前节点是定义菜单的页面。结果,菜单显示了物理站点地图文件中无意义的占位符标题,而不是正确的标题。
尝试 2 - 编写我自己的站点地图提供程序。我不想重复所有默认行为,因此我尝试从默认提供程序派生,如下所示。
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
这不起作用,因为在站点中导航时似乎并不经常调用 GetChildNodes
。
尝试 3 - 尝试在数据加载到内存后立即修复数据,而不是在访问数据时。
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
这似乎有效。但是,我担心 BuildSiteMap
方法中对 GetAllNodes
的调用。在我看来,仅仅为了修复一个值而将所有数据递归地拉入内存似乎是错误的。另外,我无法控制何时调用 BuildSiteMap
。我更喜欢类似于尝试 1 的方法,即在第一次需要节点数据时按需调用。
尝试 4(新) - 与尝试 2 类似,但覆盖与读取数据有关的所有虚拟成员(CurrentNode
、FindSiteMapNode
、FindSiteMapNodeFromKey
、GetChildNodes
、GetCurrentNodeAndHintAncestorNodes
、GetCurrentNodeAndHintNeighborhoodNodes
、GetParentNode
、 GetParentNodeRelativeToCurrentNodeAndHintDownFromParent
、GetParentNodeRelativeToNodeAndHintDownFromParent
、HintAncestorNodes
、HintNeighborhoodNodes
),尝试在某处拦截动态节点的读取。
这不起作用。我将调试语句放入所有覆盖的成员中,当数据绑定到下拉列表时,似乎根本没有调用它们。我能想到的唯一解释是,在 BuildSiteMap
调用期间,节点全部读入内存,因此 SiteMapNode
在枚举时不会命中提供程序类子节点。
有人有更好的建议吗?
We have a website that uses a bog-standard default sitemap with security trimming as follows:
<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>
All very well, but a request has come in to change the Title
of one node based on some back-end criteria. Sounds like a simple thing, but apparently not.
Attempt 1 - Handling the SiteMapResolve
event. It doesn't appear to matter where this event is handled, I have shown it in Global.asax
merely because that was one of the places I tried it and it worked.
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
This worked fine when the relevant page was navigated to, but unfortunately part of the site navigation involves a combo box that is data-bound to the site map like this:
<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" />
When this menu is rendered, the SiteMapResolve
event does not fire for any of the contents because the current node is the page on which the menu is defined. As a result, the menu shows the nonsense placeholder title from the physical sitemap file rather than the correct title.
Attempt 2 - Writing my own sitemap provider. I didn't want to duplicate all the default behaviour, so I tried deriving from the default provider as follows.
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
This doesn't work because GetChildNodes
doesn't appear to be called very often when navigating around the site.
Attempt 3 - Try to fix the data immediately after it's loaded into memory, rather than when it's accessed.
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
This appears to work. However, I'm worried about the call to GetAllNodes
in the BuildSiteMap
method. It just seems wrong to me to recursively pull all data into memory just to fix up one value. Also, I have no control over when BuildSiteMap
is called. I would prefer something more like Attempt 1, that is called on demand when the node data is first required.
Attempt 4 (NEW) - Like Attempt 2, but overriding all virtual members that are to do with reading data (CurrentNode
, FindSiteMapNode
, FindSiteMapNodeFromKey
, GetChildNodes
, GetCurrentNodeAndHintAncestorNodes
, GetCurrentNodeAndHintNeighborhoodNodes
, GetParentNode
, GetParentNodeRelativeToCurrentNodeAndHintDownFromParent
, GetParentNodeRelativeToNodeAndHintDownFromParent
, HintAncestorNodes
, HintNeighborhoodNodes
), to try to intercept the reading of the dynamic node somewhere.
This did not work. I put debug statements in all the overridden members, and it seems that none of them at all are called when data binding to the dropdown list. The only explanation I can think of is that the nodes are all read into memory in one go during the BuildSiteMap
call, so that the SiteMapNode
is not hitting the provider class when enumerating child nodes.
Does anyone have any better suggestions?
发布评论
评论(4)
在我们的自定义 SiteMapProvider 中,我们重写 BuildSiteMap 方法并手动构建 SiteMapNode。要更改和/或添加自定义属性,我们通过创建 NameValueCollection 并将其传递给 SiteMapNode 构造函数来将自定义属性添加到 SiteMapNodes。
In our Custom SiteMapProvider we override the BuildSiteMap Method and construct the SiteMapNodes manually. To change and/or add custom properties we add custom attributes to the SiteMapNodes by create a NameValueCollection and add pass this to the SiteMapNode Constructor.
您非常接近尝试#2 - 您只需要覆盖 GetParentNode 和 FindSiteMapNode 以及。
You're pretty close with attempt #2 - you just also need to override GetParentNode and FindSiteMapNode as well.
感谢马克和雷克斯的建议。我最终所做的就是留下站点地图提供程序,只修复母版页中的一个节点,因此:
但是,我已经接受了马克的回答,因为如果事实证明需要进行更广泛的修改,我将这样做将来要做的。
Thanks to Mark and Rex for the suggestions. What I ended up doing was leaving the sitemap provider alone and just fixing up the one node in the master page, thus:
However, I have accepted Mark's answer, because that's how I will do it if it turns out that more extensive modifications need to be done in future.
感谢克里斯蒂安的研究。根据您的结果,我提出了以下可能对其他人有帮助的建议:
Thanks Christian for your research. Using your results, I've come up with the following that may help others: