1

MVC サイトがあり、Autofac で MVCSiteMapProvider 4.4.3 を使用しています。XML と属性を組み合わせてサイトを構築しています。数百の動的ノードがあり、セキュリティ トリミングが有効になっています。この 1 年間でサイトが大きくなり、約 120 のコントローラーが配置されました。すべてのコントローラーは、役割などごとに異なる承認属性を使用して保護されています。

私たちのレイアウトでは、@Html.MvcSiteMap().SiteMapPath()これを呼び出すと、ページの読み込み時間が約 950 ミリ秒長くなります。この行を削除すると、ページがほぼ瞬時に読み込まれます。

以前はメニューの読み込みに数秒かかっていましたが、RenderAction に入れ、結果をキャッシュするだけで、その問題は大幅に修正されました。

これは一般的なパフォーマンスですか?SiteMapPath のパフォーマンスを向上させる明白な方法や、パフォーマンスが非常に低下する可能性のあるものはありますか?

ページをリロードすると、2 回目は 1 回目と同じくらい時間がかかります

約 10 ページをブラウジングしてプロファイリングしただけですが、CPU サイクルの約 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 名前空間への呼び出しの合計は 4 億 3,400 万でしたが、すべての独自のコード名前空間では 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"));
        }
    }
}

数百のノードを追加する1つの動的ノードプロバイダーがあります(オフにすると高速になりますが、それほど大きくはありません)

    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 に答える