Поставщик карты сайта MVC - производительность SiteMapPath очень низкая?

У меня есть сайт MVC, и мы используем MVCSiteMapProvider 4.4.3 с Autofac. Мы создаем наш сайт, используя смесь XML и атрибутов. У нас есть несколько сотен динамических узлов, и мы включили ограничение безопасности. За последний год сайт стал больше с примерно 120 контроллерами. Все контроллеры защищены с помощью атрибутов авторизации, которые различаются в зависимости от роли и т. Д.

В нашем макете мы называем @Html.MvcSiteMap().SiteMapPath() это добавляет примерно 950мс к времени загрузки страницы. Если мы удалим строку, наши страницы загружаются практически мгновенно.

Наше меню загружалось секунду, но мы помещали его в RenderAction и просто кэшировали результат, который в значительной степени устранил эту проблему.

Это общее представление? Есть ли очевидный способ повысить производительность SiteMapPath или вещи, которые могут привести к снижению производительности?

Если мы перезагрузим страницу, это займет столько же времени, сколько и первый

Просто просматривая около десяти страниц и профилируя, но около 70% циклов ЦП, похоже, пошли на:

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 составило 434 миллиона против 1 миллиона для всех наших собственных пространств имен кода.

Наш модуль 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;
                    }
                }
            }
        }
    }
}

Конфигурация SiteMap:

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

Остальные узлы добавляются с использованием атрибутов в действиях контроллера

Мы работаем в режиме релиза

2 ответа

Решение

Я уверен, что это было вызвано проблемами с нашей конфигурацией / настройкой карты сайта. Мы полагаемся на ошибку в SiteMap, которая ранее сохраняла данные маршрута. Однако в v4 это было исправлено.

Проблема с производительностью была вызвана, когда мы посетили действия, в которых был размещен атрибут узла, но которые не содержали требуемых сохраненных данных маршрута. Поставщик Sitemap, кажется, немного сошел с ума, я думаю, пытаясь найти соответствующий узел / маршрут.

Сейчас мы размещаем большое количество preserveRouteData в коде, и это устраняет проблемы. В идеале мы хотели бы сейчас создать динамические узлы (поскольку вся наша карта сайта основана на различных основных объектах, например людях с множеством дочерних узлов, прикрепленных к ним), чтобы избежать этого. Однако у нас есть проблема, поскольку мы также хотим использовать атрибуты для добавления дочерних элементов в динамические узлы, см. Здесь

Если ваш сайт работает в режиме отладки, это приведет к снижению производительности с помощью помощников HTML. Обратите внимание, что это для V3 и V4 должны были улучшить это.

от http://mvcsitemap.codeplex.com/wikipage?title=HtmlHelper%20functions

Известные проблемы с производительностью и решение Можно заметить снижение производительности при работе с функциями HtmlHelper из Visual Studio. Это связано с тем, что во время отладки внутреннее кэширование в ASP.NET MVC не происходит в отношении визуализации представления. Решением этой проблемы является запуск приложения в режиме выпуска или изменение Web.config для запуска в режиме выпуска:

<compilation debug="false">
Другие вопросы по тегам