1

我有一个 MVC 站点,我们正在使用 MVCSiteMapProvider 4.4.3 和 Autofac。我们使用 XML 和属性的混合构建我们的站点。我们有几百个动态节点,并且启用了安全修整。去年,站点变得越来越大,大约有 120 个控制器。所有控制器都使用因角色而异的授权属性等进行保护。

在我们的布局中,我们称之为@Html.MvcSiteMap().SiteMapPath()增加了大约 950 毫秒的页面加载时间。如果我们删除该行,我们的页面几乎会立即加载。

我们的菜单过去需要一秒钟才能加载 - 但是我们将它放在 RenderAction 中并简单地缓存了在很大程度上解决了该问题的结果。

这是常见的表现吗?是否有任何明显的方法可以提高 SiteMapPath 的性能或可能导致性能如此糟糕的事情

如果我们重新加载页面,第二次和第一次一样长

仅浏览大约十页并进行分析,但大约 70% 的 CPU 周期似乎已经转到:

MvcSiteMapProvider.Caching.RequestCache.GetValue(String)
MvcSiteMapProvider.RequestCacheableSiteMapNode.GetCacheKey(String)
MvcSiteMapProvider.Collections.Specialized.RouteValueDictionary.GetCacheKey()
MvcSiteMapProvider.RequestCacheableSiteMap.GetCacheKey(String)
MvcSiteMapProvider.Web.Mvc.MvcContextFactory.CreateHttpContext(ISiteMapNode)
MvcSiteMapProvider.RequestCacheableSiteMapNode.get_AreRouteParametersPreserved()
MvcSiteMapProvider.SiteMap.GetChildNodes(ISiteMapNode)
MvcSiteMapProvider.SiteMap.FindSiteMapNodeFromControllerAction(ISiteMapNode, IDictionary[StringObject], RoutMvcSiteMapProvider.Collections.CacheableDictionary`2.ContainsKey(TKey)
eBase)
MvcSiteMapProvider.RequestCacheableSiteMap.IsAccessibleToUser(ISiteMapNode)
MvcSiteMapProvider.Collections.CacheableDictionary`2.get_ReadOperationDictionary()

对 MVCSiteMapProvder 命名空间的总调用次数为 4.34 亿次,而我们自己的代码命名空间为 100 万次。

我们的 Autofac 模块是:

    public class MvcSiteMapProviderModule : global::Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            const bool SecurityTrimmingEnabled = false;
            const bool EnableLocalization = false;
            var absoluteFileName = HostingEnvironment.MapPath("~/Mvc.sitemap");
            var absoluteCacheExpiration = TimeSpan.FromMinutes(60);

            var includeAssembliesForScan = new[] { "OnboardWeb" };

            var currentAssembly = this.GetType().Assembly;
            var siteMapProviderAssembly = typeof(SiteMaps).Assembly;
            var allAssemblies = new Assembly[] { currentAssembly, siteMapProviderAssembly };
            var excludeTypes = new Type[] 
            {
                typeof(SiteMapNodeVisibilityProviderStrategy),
                typeof(SiteMapXmlReservedAttributeNameProvider),
                typeof(SiteMapBuilderSetStrategy)
            };
            var multipleImplementationTypes = new Type[] 
            {
                typeof(ISiteMapNodeUrlResolver),
                typeof(ISiteMapNodeVisibilityProvider),
                typeof(IDynamicNodeProvider)
            };

            // Single implementations of interface with matching name (minus the "I").
            CommonConventions.RegisterDefaultConventions(
                (interfaceType, implementationType) => builder.RegisterType(implementationType).As(interfaceType).SingleInstance(),
                new Assembly[] { siteMapProviderAssembly },
                allAssemblies,
                excludeTypes,
                string.Empty);


            // Multiple implementations of strategy based extension points
            CommonConventions.RegisterAllImplementationsOfInterface(
                (interfaceType, implementationType) => builder.RegisterType(implementationType).As(interfaceType).SingleInstance(),
                multipleImplementationTypes,
                allAssemblies,
                excludeTypes,
                "^Composite");

            // Registration of internal controllers
            CommonConventions.RegisterAllImplementationsOfInterface(
                (interfaceType, implementationType) => builder.RegisterType(implementationType).As(interfaceType).AsSelf().InstancePerDependency(),
                new Type[] { typeof(IController) },
                new Assembly[] { siteMapProviderAssembly },
                new Type[0],
                string.Empty);

            // Visibility Providers
            builder.RegisterType<SiteMapNodeVisibilityProviderStrategy>()
                .As<ISiteMapNodeVisibilityProviderStrategy>()
                .WithParameter("defaultProviderName", string.Empty);
            //.WithParameter("defaultProviderName", "MvcSiteMapProvider.FilteredSiteMapNodeVisibilityProvider, MvcSiteMapProvider");

            //builder.RegisterType<BreadCrumbOnlyVisibilityProvider>()
            //    .As<ISiteMapNodeVisibilityProvider>().InstancePerLifetimeScope();

            // Pass in the global controllerBuilder reference
            builder.RegisterInstance(ControllerBuilder.Current)
                   .As<ControllerBuilder>();

            builder.RegisterType<BuildManagerAdaptor>()
                   .As<IBuildManager>();

            builder.RegisterType<ControllerBuilderAdaptor>()
                   .As<IControllerBuilder>();

            builder.RegisterType<ControllerTypeResolverFactory>()
                .As<IControllerTypeResolverFactory>()
                .WithParameter("areaNamespacesToIgnore", new string[0]);

            // Configure Security
            builder.RegisterType<AuthorizeAttributeAclModule>()
                .Named<IAclModule>("authorizeAttributeAclModule");

            builder.RegisterType<XmlRolesAclModule>()
                .Named<IAclModule>("xmlRolesAclModule");
            builder.RegisterType<CompositeAclModule>()
                .As<IAclModule>()
                .WithParameter(
                    (p, c) => p.Name == "aclModules",
                    (p, c) => new[]
                        {
                            c.ResolveNamed<IAclModule>("authorizeAttributeAclModule"),
                            c.ResolveNamed<IAclModule>("xmlRolesAclModule")
                        });



            builder.RegisterInstance(System.Runtime.Caching.MemoryCache.Default)
                   .As<System.Runtime.Caching.ObjectCache>();

            builder.RegisterGeneric(typeof(RuntimeCacheProvider<>))
                .As(typeof(ICacheProvider<>));

            builder.RegisterType<RuntimeFileCacheDependency>()
                .Named<ICacheDependency>("cacheDependency1")
                .WithParameter("fileName", absoluteFileName);

            builder.RegisterType<CacheDetails>()
                .Named<ICacheDetails>("cacheDetails1")
                .WithParameter("absoluteCacheExpiration", absoluteCacheExpiration)
                .WithParameter("slidingCacheExpiration", TimeSpan.MinValue)
                .WithParameter(
                    (p, c) => p.Name == "cacheDependency",
                    (p, c) => c.ResolveNamed<ICacheDependency>("cacheDependency1"));

            // Configure the visitors
            builder.RegisterType<UrlResolvingSiteMapNodeVisitor>()
                   .As<ISiteMapNodeVisitor>();

            // Prepare for our node providers
            builder.RegisterType<FileXmlSource>()
                .Named<IXmlSource>("xmlSource1")
                .WithParameter("fileName", absoluteFileName);

            builder.RegisterType<SiteMapXmlReservedAttributeNameProvider>()
                .As<ISiteMapXmlReservedAttributeNameProvider>()
                .WithParameter("attributesToIgnore", new string[0]);


            // Register the sitemap node providers
            builder.RegisterType<XmlSiteMapNodeProvider>()
                .Named<ISiteMapNodeProvider>("xmlSiteMapNodeProvider1")
                .WithParameter("includeRootNode", true)
                .WithParameter("useNestedDynamicNodeRecursion", false)
                .WithParameter(
                    (p, c) => p.Name == "xmlSource",
                    (p, c) => c.ResolveNamed<IXmlSource>("xmlSource1"));

            builder.RegisterType<ReflectionSiteMapNodeProvider>()
                .Named<ISiteMapNodeProvider>("reflectionSiteMapNodeProvider1")
                .WithParameter("includeAssemblies", includeAssembliesForScan)
                .WithParameter("excludeAssemblies", new string[0]);

            builder.RegisterType<CompositeSiteMapNodeProvider>()
                .Named<ISiteMapNodeProvider>("siteMapNodeProvider1")
                .WithParameter(
                    (p, c) => p.Name == "siteMapNodeProviders",
                    (p, c) => new[]
                        {
                            c.ResolveNamed<ISiteMapNodeProvider>("xmlSiteMapNodeProvider1"),
                            c.ResolveNamed<ISiteMapNodeProvider>("reflectionSiteMapNodeProvider1")
                        });

            // Register the sitemap builders
            builder.RegisterType<SiteMapBuilder>()
                .Named<ISiteMapBuilder>("siteMapBuilder1")
                .WithParameter(
                    (p, c) => p.Name == "siteMapNodeProvider",
                    (p, c) => c.ResolveNamed<ISiteMapNodeProvider>("siteMapNodeProvider1"));


            // Configure the builder sets
            builder.RegisterType<SiteMapBuilderSet>()
                   .Named<ISiteMapBuilderSet>("builderSet1")
                   .WithParameter("instanceName", "default")
                   .WithParameter("securityTrimmingEnabled", SecurityTrimmingEnabled) 
                   .WithParameter("enableLocalization", EnableLocalization)
                   .WithParameter(
                        (p, c) => p.Name == "siteMapBuilder",
                        (p, c) => c.ResolveNamed<ISiteMapBuilder>("siteMapBuilder1"))
                   .WithParameter(
                        (p, c) => p.Name == "cacheDetails",
                        (p, c) => c.ResolveNamed<ICacheDetails>("cacheDetails1"));

            builder.RegisterType<SiteMapBuilderSetStrategy>()
                .As<ISiteMapBuilderSetStrategy>()
                .WithParameter(
                    (p, c) => p.Name == "siteMapBuilderSets",
                    (p, c) => c.ResolveNamed<IEnumerable<ISiteMapBuilderSet>>("builderSet1"));
        }
    }
}

我们有一个动态节点提供者添加了几百个节点(如果我们关闭它会更快但不是很明显)

    public class LocationsDynamicNodeProvider : DynamicNodeProviderBase
    {
        private List<Country> countries;

        /// <summary>
        /// Lazy loading of countries. Only create the graph when we actually need it.
        /// Previously it was in the constructor, but for lightweight object composition we must
        /// not do any work in the constructor.
        /// </summary>
        /// <returns></returns>
        private List<Country> GetCountries()
        {
            if (countries == null)
            {
                var countryRepository = DependencyResolver.Current.GetService<ICountryRepository>();
                countries = countryRepository.AllWithLocations().ToList();
            }

            return countries;
        }


        public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
        {
            countries = GetCountries();
            foreach (var country in countries)
            {
                var countrynode = new DynamicNode
                                      {
                                          Title = country.Name,
                                          Controller = "Assets",
                                          Action = "Index",
                                          Area = "OnboardAsset",
                                          RouteValues = new RouteValueDictionary
                                                            {
                                                                { "countryname", country.Name }, 
                                                                { "locationname", "" }, 
                                                                { "sitename", "" }
                                                            },
                                          ParentKey = "All Assets",
                                          Key = "countrynode_" + country.CountryId
                                      };

                yield return countrynode;
                foreach (var site in country.Sites)
                {
                    var sitenode = new DynamicNode
                    {
                        Title = site.Name,
                        Controller = "Assets",
                        Action = "Index",
                        Area = "OnboardAsset",
                        RouteValues =
                            new RouteValueDictionary()
                            {
                                { "countryname", country.Name }, 
                                { "sitename", site.Name }, 
                                { "locationname", "" }
                            },
                        ParentKey = "countrynode_" + country.CountryId,
                        Key = "sitenode_" + site.SiteId
                    };


                    yield return sitenode;
                    foreach (var location in site.Locations)
                    {
                        var locationNode = new DynamicNode
                        {
                            Title = location.Name,
                            Controller = "Assets",
                            Action = "Index",
                            Area = "OnboardAsset",
                            RouteValues =
                                new RouteValueDictionary 
                                { 
                                { "countryname", country.Name }, 
                                { "sitename", site.Name }, 
                                { "locationname", location.Name } 
                                },
                            ParentKey = "sitenode_" + site.SiteId
                        };

                        yield return locationNode;
                    }
                }
            }
        }
    }
}

站点地图配置:

<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="Home" controller="HomePage" action="Index" key="Home">

    <mvcSiteMapNode title="People" key="PeopleTop" controller="People" action="Index" area="OnboardTeam" >
      <mvcSiteMapNode title="All People" key="PeopleIndex" controller="People" action="Index" area="OnboardTeam" visibility="hideChildren" />
    </mvcSiteMapNode>

    <mvcSiteMapNode title="Assets" key="Assets" controller="Home" action="Index" area="OnboardAsset">
      <mvcSiteMapNode title="All Assets" key="All Assets" controller="Assets" action="Index" route="AllAssets">
        <mvcSiteMapNode title="LocationNodes" dynamicNodeProvider="Onboard.Web.Infrastructure.Menu.LocationsDynamicNodeProvider, OnboardWeb" />
      </mvcSiteMapNode>
    </mvcSiteMapNode>

    <mvcSiteMapNode title="Jobs" controller="Jobs" action="Index" area="Core" key="Jobs" visibility="hideChildren" />

    <mvcSiteMapNode title="Reports" key="Report" clickable="false">
      <mvcSiteMapNode title="Certifications" key="Report_Certifications" clickable="false" />
    </mvcSiteMapNode>

    <mvcSiteMapNode title="CRM" controller= "CRM" area="CRM" key="CRM" action="Index">
    </mvcSiteMapNode>

    <mvcSiteMapNode title="PO" key="PO" action="GeneralList" controller= "PurchaseOrders">
      <mvcSiteMapNode title="Purchase Orders" action="GeneralList" controller= "PurchaseOrders" area="PO" key="PO_List" />
    </mvcSiteMapNode>

    <mvcSiteMapNode title="Training" key="OnboardTraining" controller="PersonTrainingBookings" action="Index" />

    <mvcSiteMapNode title="Document Store" key="Documents" area="Documents" controller="DocumentStore" action="Browse" />

    <mvcSiteMapNode title="Admin" key="Admin" roles="Administrator" clickable="false">
      <mvcSiteMapNode title="Competence" key="Competences" area="OnboardTeam" controller="Competences" action="Index" />
      <mvcSiteMapNode title="Certification" key="Certifications" area="OnboardTeam" controller="Certification" action="Index" />
      <mvcSiteMapNode title="Supporting Entities" key="LookupTable" clickable="false" />
      <mvcSiteMapNode title="Entity Types" key="LookupTypes" clickable="false" />
      <mvcSiteMapNode title="Users and Teams" key="UsersAndTeams" area="Core" controller="UserManagement" action="Index" clickable="false" />
      <mvcSiteMapNode title="Companies" key="Organisations" area="Core" controller="Companies" action="Index" clickable="false" />
      <mvcSiteMapNode title="Geographic Data" key="Geographic" area="Core" controller="Countries" action="Index" clickable="false" />
    </mvcSiteMapNode>

  </mvcSiteMapNode>

</mvcSiteMap>

其余节点正在使用控制器操作上的属性添加

我们在发布模式下运行

4

2 回答 2

2

我很确定这是由我们的站点地图配置/设置问题引起的。我们一直依赖我认为SiteMap 中的一个错误,该错误以前保留了路线数据。但是在 v4 中,这已修复。

当我们访问放置了节点属性但不包含所需的保留路由数据的操作时,会导致性能问题。站点地图提供商似乎有点疯狂,我认为试图找到合适的节点/路线。

我们现在在整个代码中放置了大量的 preserveRouteData 并解决了这些问题。理想情况下,我们现在希望创建动态节点(因为我们的整个站点地图基于各种主要实体,例如挂在其上的有很多子节点的人)来避免这种情况。但是我们有问题,因为我们还想使用属性将子节点添加到动态节点,请参见此处

于 2013-10-10T01:10:26.683 回答
0

如果您的站点在调试模式下运行,它将导致 html 帮助程序的性能下降。请注意,这是针对 V3 的,V4 应该对此进行了改进。

来自 http://mvcsitemap.codeplex.com/wikipage?title=HtmlHelper%20functions

已知的性能问题和解决方案 使用 Visual Studio 中的 HtmlHelper 函数时可能会注意到性能下降。这是因为在调试期间,在 ASP.NET MVC 内部不会发生关于视图呈现的缓存。解决方案是在发布模式下运行应用程序或将 Web.config 更改为在发布模式下运行:

<compilation debug="false">
于 2014-02-20T16:46:41.430 回答