MVC 站点地图提供程序 - SiteMapPath 性能很慢? [英] MVC Site Map Provider - SiteMapPath Performance Very Slow?

查看:50
本文介绍了MVC 站点地图提供程序 - SiteMapPath 性能很慢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an MVC site and we are using MVCSiteMapProvider 4.4.3 with Autofac. We construct our site using a mixture of XML and Attributes. We have a few hundred dynamic nodes and we have security trimming enabled. Site has been getting larger over the last year with approximately 120 controllers. All controllers are secured using authorize attributes that vary per role etc.

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

In our layout we call @Html.MvcSiteMap().SiteMapPath() this adds approximately 950ms onto page load time. If we remove the line our pages load almost instantly.

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

Our menu used to take a second to load - however we put it in a RenderAction and simply cached the result which largely fixed that issue.

这是常见的表现吗?有没有什么明显的方法可以提高 SiteMapPath 的性能或者可能导致性能这么差的东西

Is this common performance? Is there any obvious way to enhance performance for SiteMapPath or things that may cause the performance to be so poor

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

If we reload the page it takes as long the second time as the first

仅仅浏览了大约 10 个页面并进行了分析,但大约 70% 的 CPU 周期似乎已经去了:

Just browsing about ten pages and profiling but about 70% of CPU cyles seem to have gone to:

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 万次.

The total calls to the MVCSiteMapProvder Namespace were 434 million vs 1 million for all of our own code Namespaces.

我们的 Autofac 模块是:

Our Autofac module is:

    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"));
        }
    }
}

我们有一个动态节点提供程序添加了几百个节点(如果我们将其关闭,速度会更快,但不是很明显)

We have one dynamic node provider adding a few hundred nodes (if we turn it off it is faster but not significantly so)

    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>

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

Rest of nodes are adding using attributes on controller actions

我们在发布模式下运行

推荐答案

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

I am pretty sure this was caused by issues with our sitemap config/setup. We have been relying on I think a bug in SiteMap that was previosuly preserving route data. However in v4 this was fixed.

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

Performance issue was caused when we visited actions which had a node attribute placed on but did not contain required preserved route data. Sitemap provider seemed to go a bit nuts I think trying to locate an appropriate node/route.

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

We are now placing a lot of preserveRouteData throughout code and it fixes the issues. We ideally would like to now create dynamic nodes (as our whole sitemap is based on various major entities e.g. People with lots of child nodes hung off them) to avoid this. However we have issue as we also want to use attributes to add children to dynamic nodes see here

这篇关于MVC 站点地图提供程序 - SiteMapPath 性能很慢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆