默认的XmlSiteMapProvider实现不能使用SiteMap.FindSiteMapNode?

时间:2014-05-08 19:22:44

标签: mvcsitemapprovider asp.net-mvc-sitemap

我只是将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文件中定义urlurlResolver属性。这些变量在生成节点时设置为空字符串。

此节点传递给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;
}

最后,urlTable没有添加任何网址,导致FindSiteMapNode找不到任何内容。

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

1 个答案:

答案 0 :(得分:0)

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

如果你解释一下你想要完成什么,这可能会有所帮助。

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

<强>更新

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

使用数据有两个阶段。第一阶段(ISiteMapBuilder和ISiteMapNodeProvider)从各种源(XML,.NET属性,DynamicNodeProviders和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帮助程序自定义,请查看this answer以获取更多帮助。

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