0

我只是将 MvcSiteMapProvider 从 v3 升级到 v4.6.3。

我看到升级说明表明:

一般来说,任何对的引用System.Web.SiteMap.Provider都需要更新为MvcSiteMapProvider.SiteMaps.Current

我正在尝试使用以下方法获取站点地图节点: SiteMaps.Current.FindSiteMapNode(rawUrl)

但它总是返回 null

我查看了代码。在站点地图中,它实际上是在调用该函数:

    protected virtual ISiteMapNode FindSiteMapNodeFromUrlMatch(IUrlKey urlToMatch)
    {
        if (this.urlTable.ContainsKey(urlToMatch))
        {
            return this.urlTable[urlToMatch];
        }

        return null;
    }

它试图在urlTable中找到匹配项。

我正在使用XmlSiteMapProvider.

它定义var url = node.GetAttributeValue("url");

siteMapNode.Url = url;
siteMapNode.UrlResolver = node.GetAttributeValue("urlResolver");

因此,如果我没有在 .sitemap 文件中定义url或属性。urlResolver这些变量在生成节点时设置为空字符串。

当这个节点被传递给AddNode函数时SiteMap

添加节点时

bool isMvcUrl = string.IsNullOrEmpty(node.UnresolvedUrl) && this.UsesDefaultUrlResolver(node);

此代码将检查是否存在urlurlResolver

// Only store URLs if they are clickable and are configured using the Url
// property or provided by a custom URL resolver.
if (!isMvcUrl && node.Clickable)
{
    url = this.siteMapChildStateFactory.CreateUrlKey(node);

    // Check for duplicates (including matching or empty host names).
    if (this.urlTable
        .Where(k => string.Equals(k.Key.RootRelativeUrl, url.RootRelativeUrl, StringComparison.OrdinalIgnoreCase))
        .Where(k => string.IsNullOrEmpty(k.Key.HostName) || string.IsNullOrEmpty(url.HostName) || string.Equals(k.Key.HostName, url.HostName, StringComparison.OrdinalIgnoreCase))
        .Count() > 0)
    {
        var absoluteUrl = this.urlPath.ResolveUrl(node.UnresolvedUrl, string.IsNullOrEmpty(node.Protocol) ? Uri.UriSchemeHttp : node.Protocol, node.HostName);
        throw new InvalidOperationException(string.Format(Resources.Messages.MultipleNodesWithIdenticalUrl, absoluteUrl));
    }
}
// Add the URL
if (url != null)
{
    this.urlTable[url] = node;
}

最后没有 url 添加到urlTable,导致FindSiteMapNode找不到任何东西。

我不确定是否需要特定的配置。或者我应该实施自定义XmlSiteMapProvider只是添加网址。

4

1 回答 1

0

ISiteMapNodeProvider 实例无法使用 FindSiteMapNode 函数有两个原因。您已经发现的第一个问题是,只有在节点配置中明确设置 url 属性时,才能通过 URL 查找。第二个原因是 SiteMapBuilder 在所有 ISiteMapNodeProvider 实例完成运行之前不会将任何节点添加到 SiteMap,因此无论如何将 URL 添加到 URL 表都是没有实际意义的。

如果您解释您要完成的工作,可能会有所帮助。

ISiteMapNodeProvider 类可以完全控制添加到 SiteMapNode 实例的数据,并且它们还可以访问其父 SiteMapNode 实例。这通常是填充数据所需的全部内容。不支持在填充数据时从 SiteMap 对象查找另一个 SiteMapNode。但只要您感兴趣的节点填充在同一个 ISiteMapNodeProvider 实例中,您就可以稍后通过将其存储在变量中来获取对它的引用。

更新

好的,我重读了你的问题和评论,现在看来你找错地方了。MvcSiteMapProvider v4 不再基于 Microsoft 的 SiteMap 提供程序模型,因此使用 XmlSiteMapProvider 没有意义,因为它会回避整个实现。这可能有意义的唯一情况是,如果您有一个混合的 ASP.NET 和 ASP.NET MVC 应用程序,您希望在它们之间保持一致的菜单结构。请参阅从 v3 升级到 v4

处理数据有两个阶段。第一阶段(ISiteMapBuilder 和 ISiteMapNodeProvider)从各种来源(XML、.NET 属性、DynamicNodeProvider 和 ISiteMapNodeProvider 的自定义实现)加载数据,并将其添加到从 SiteMap 对象开始的对象图中。与 Microsoft 的模型非常相似,此数据存储在共享缓存中,并且仅在缓存过期时才加载。这是您一直关注的阶段,在这里查找节点绝对没有意义。

第二阶段是当个人请求访问数据时。这是基于 URL 查找数据可能有意义的地方,但是已经有一个内置的 CurrentNode 属性可以找到与当前 URL 匹配的节点(或者更可能是当前路由,因为我们正在处理 MVC),这在大多数情况下是寻找节点的最佳方法。每个节点都有一个 ParentNode 和 ChildNodes 属性,可用于从那里向上或向下移动树。

在此第二阶段,您可以在 Application_Start 事件之后的任何时间点访问 SiteMap 数据,例如在控制器操作中、内置 HTML 帮助器之一、/Views/Shared/DisplayTemplates/目录中的 HTML 帮助器模板或自定义 HTML 帮助器中。这是应用程序生命周期中的点,您可能会调用这些行SiteMaps.Current.FindSiteMapNode(rawUrl)或(更有可能)SiteMaps.Current.CurrentNode获取节点的实例,以便您可以检查其 Attributes 属性(自定义属性)。

public ActionResult About()
{
    ViewBag.Message = "Your app description page.";

    var currentNode = MvcSiteMapProvider.SiteMaps.Current.CurrentNode;

    string permission = currentNode.Attributes.ContainsKey("permission") ? currentNode.Attributes["permission"].ToString() : string.Empty;
    string programs = currentNode.Attributes.ContainsKey("programs") ? currentNode.Attributes["programs"].ToString() : string.Empty;
    string agencies = currentNode.Attributes.ContainsKey("agencies") ? currentNode.Attributes["agencies"].ToString() : string.Empty;

    // Do something with the custom attributes of the About page here

    return View();
}

自定义属性最常见的用法是在自定义 HTML 帮助器模板中使用它们。这是/Views/Shared/DisplayTemplates/SiteMapNodeModel.cshtml显示自定义属性的模板的自定义版本。请注意,此模板由 Menu、SiteMapPath 和 SiteMap HTML 帮助程序递归调用。如果您打算执行 HTML 帮助程序自定义,请查看此答案以获得更多帮助。

@model MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
@using System.Web.Mvc.Html
@using MvcSiteMapProvider.Web.Html.Models

@if (Model.IsCurrentNode && Model.SourceMetadata["HtmlHelper"].ToString() != "MvcSiteMapProvider.Web.Html.MenuHelper")  { 
    <text>@Model.Title</text>
} else if (Model.IsClickable) {
    if (string.IsNullOrEmpty(Model.Description))
    {
        <a href="@Model.Url">@Model.Title</a>
    }
    else
    {
        <a href="@Model.Url" title="@Model.Description">@Model.Title</a>
    }
} else { 
    <text>@Model.Title</text>
}

@string permission = Model.Attributes.ContainsKey("permission") ? Model.Attributes["permission"].ToString() : string.Empty
@string programs = Model.Attributes.ContainsKey("programs") ? Model.Attributes["programs"].ToString() : string.Empty
@string agencies = Model.Attributes.ContainsKey("agencies") ? Model.Attributes["agencies"].ToString() : string.Empty

<div>@permission</div>
<div>@programs</div>
<div>@agencies</div>
于 2014-05-09T07:47:09.520 回答