如何使用MVCSitemapProvider从ASP.NET MVC4中的父站点地图链接到子站点地图文件?

时间:2015-02-03 11:02:46

标签: asp.net-mvc-4 seo sitemap mvcsitemapprovider

我在MVC4中使用MVCSitemapProvider by Maarten Balliauw和Ninject DI。作为一个大型Web应用程序,枚举记录以生成站点地图xml帐户占页面加载时间的70%。为此,我开始为每个n级动态节点提供程序使用新的站点地图文件。

<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0" xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0 MvcSiteMapSchema.xsd">
    <mvcSiteMapNode title="$resources:SiteMapLocalizations,HomeTitle" description="$resources:SiteMapLocalizations,HomeDescription" controller="Controller1" action="Home" changeFrequency="Always" updatePriority="Normal" metaRobotsValues="index follow noodp noydir"><mvcSiteMapNode title="$resources:SiteMapLocalizations,AboutTitle" controller="ConsumerWeb" action="Aboutus"/>
    <mvcSiteMapNode title="Sitemap" controller="Consumer1" action="SiteMap"/><mvcSiteMapNode title=" " action="Action3" controller="Consumer2" dynamicNodeProvider="Comp.Controller.Utility.NinjectModules.PeopleBySpecDynamicNodeProvider, Comp.Controller.Utility" />
    <mvcSiteMapNode title="" siteMapFile="~/Mvc2.sitemap"/>
    </mvcSiteMapNode>
    </mvcSiteMap>

但是,它似乎不起作用。对于localhost:XXXX / sitemap.xml,Mvc2.sitemap中的子节点似乎无法加载。

1 个答案:

答案 0 :(得分:0)

siteMapFile不是MvcSiteMapProvider中的有效XML属性(尽管您可以将其用作自定义属性),因此我不确定您要执行此操作的指南。但是,底线是没有功能加载“子站点地图文件”,即使有,它也无助于解决您的问题,因为所有节点都会立即加载到内存中。实际上,在普通服务器上存在大约10,000到15,000个节点的上限。

您描述的问题是一个已知问题。 issue #258中提供了一些可能有帮助或可能没有帮助的提示。

我们正在使用new XML sitemap implementation,它允许您将XML站点地图直接连接到数据源,这可用于解决此问题(至少就XML站点地图而言)。此实现是基于流的,具有可以直接绑定到数据源的分页,并且可以无缝地分页到多个表,因此它非常有效。然而,虽然有一个工作原型,但它仍然有一段时间没有被发布。

如果您需要早点而不是晚些时候,欢迎您从this branch获取原型。

您需要一些代码才能将其连接到您的应用程序中(这可能会在官方发布时发生变化)。我创建了一个demo project here

<强>的Application_Start

var registrar = new MvcSiteMapProvider.Web.Routing.XmlSitemapFeedRouteRegistrar();
registrar.RegisterRoutes(RouteTable.Routes, "XmlSitemap2");

<强> XmlSitemap2Controller

using MvcSiteMapProvider.IO;
using MvcSiteMapProvider.Web.Mvc;
using MvcSiteMapProvider.Xml.Sitemap.Configuration;
using System.Web.Mvc;

public class XmlSitemap2Controller : Controller
{
    private readonly IXmlSitemapFeedResultFactory xmlSitemapFeedResultFactory;

    public XmlSitemap2Controller()
    {
        var builder = new XmlSitemapFeedStrategyBuilder();

        var xmlSitemapFeedStrategy = builder
            .SetupXmlSitemapProviderScan(scan => scan.IncludeAssembly(this.GetType().Assembly))
            .AddNamedFeed("default", feed => feed.WithMaximumPageSize(5000).WithContent(content => content.Image().Video()))
            .Create();

        var outputCompressor = new HttpResponseStreamCompressor();

        this.xmlSitemapFeedResultFactory = new XmlSitemapFeedResultFactory(xmlSitemapFeedStrategy, outputCompressor);
    }

    public ActionResult Index(int page = 0, string feedName = "")
    {
        var name = string.IsNullOrEmpty(feedName) ? "default" : feedName;
        return this.xmlSitemapFeedResultFactory.Create(page, name);
    }
}

<强> IXmlSiteMapProvider

您需要一个或多个IXmlSitemapProvider实施。为方便起见,有一个基类XmlSiteMapProviderBase。这些类似于在MVC中创建控制器。

using MvcSiteMapProvider.Xml.Sitemap;
using MvcSiteMapProvider.Xml.Sitemap.Specialized;
using System;
using System.Linq;

public class CategoriesXmlSitemapProvider : XmlSitemapProviderBase, IDisposable
{
    private EntityFramework.MyEntityContext db = new EntityFramework.MyEntityContext();

    // This is optional. Don't override it if you don't want to use last modified date.
    public override DateTime GetLastModifiedDate(string feedName, int skip, int take)
    {
        // Get the latest date in the specified page
        return db.Category.OrderBy(x => x.Id).Skip(skip).Take(take).Max(c => c.LastUpdated);
    }

    public override int GetTotalRecordCount(string feedName)
    {
        // Get the total record count for all pages
        return db.Category.Count();
    }

    public override void GetUrlEntries(IUrlEntryHelper helper)
    {
        // Do not call ToList() on the query. The idea is that we want to force
        // EntityFramework to use a DataReader rather than loading all of the data
        // at once into RAM.
        var categories = db.Category
            .OrderBy(x => x.Id)
            .Skip(helper.Skip)
            .Take(helper.Take);

        foreach (var category in categories)
        {
            var entry = helper.BuildUrlEntry(string.Format("~/Category/{0}", category.Id))
                .WithLastModifiedDate(category.LastUpdated)
                .WithChangeFrequency(MvcSiteMapProvider.ChangeFrequency.Daily)
                .AddContent(content => content.Image(string.Format("~/images/category-image-{0}.jpg", category.Id)).WithCaption(category.Name));

            helper.SendUrlEntry(entry);
        }
    }

    public void Dispose()
    {
        db.Dispose();
    }
}

请注意,目前没有IXmlSiteMapProvider实现从默认(或任何)SiteMap读取节点,但是创建一个类似于上面显示的那个,除了您将在SiteMap中查询节点而不是记录数据库。

或者,您可以使用第三方XML站点地图生成器。虽然,几乎所有这些都是以不可扩展的方式为大型站点设置的,并且大多数都由您来处理分页。如果他们没有流式传输节点,那么它实际上不会扩展到超过几千个URL。

您可能需要注意的其他细节是使用forcing a match技术来减少SiteMap中的节点总数。如果您使用菜单和/或SiteMap HTML帮助程序,则需要单独保留所有高级节点。但是任何没有出现的节点都是一个很好的候选者。实际上,使用这种技术几乎可以将任何数据驱动的站点减少到几十个节点,但请记住,每个被强制匹配SiteMap中多个路由的节点都意味着需要在XML站点地图中添加单个URL条目

相关问题