2013-10-03 111 views
1

我有一個MVC的網站,我們正在使用MVCSiteMapProvider 4.4.3與Autofac。我們使用XML和屬性的混合構建我們的網站。我們有幾百個動態節點,並且我們已啓用安全修整。過去一年,網站已經增加了大約120個控制器。所有控制器都使用根據角色而不同的授權屬性進行保護。MVC站點地​​圖提供程序 - SiteMapPath性能非常慢?

在我們的佈局中,我們調用@Html.MvcSiteMap().SiteMapPath(),這會在頁面加載時間內增加大約950ms。如果我們刪除該行,我們的頁面幾乎立即加載。

我們用來進行第二次加載菜單 - 但是我們把它放在一個和的RenderAction簡單緩存這在很大程度上修復了問題的結果。

這是常見的表現?是否有增強的SiteMapPath或事物表現任何明顯的方式,可能會導致性能是如此之差

如果我們刷新頁面需要第二次,只要第一

只是瀏覽約十頁剖析但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.34億VS 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> 

其餘節點正在使用屬性添加控制器操作

我們在釋放模式

+0

你們中的大多數已經張貼是專門通過請求高速緩存以提高性能的方法。你可以發佈你的節點配置(包括動態節點提供者)和你的Autofac模塊嗎?另外,如果禁用安全修整,性能如何比較? – NightOwl888

+0

已更新。看起來似乎有很多行代碼被執行。更新配置等。禁用安全修整沒有任何重大差異。 – GraemeMiller

+0

事實上,你接到這麼多的電話是非常不尋常的,我懷疑它與你的配置中的某些東西有關。您可以將您的設置與MvcMusicStore demo @ GitHub進行比較,該演示演示瞭如何與數百個動態節點和Autofac(Autofac的編譯符號集)一起工作。但最好的選擇是構建一個小型的演示項目,重現行爲,使其可以下載,並開啓一個新的問題@ GitHub。你甚至可以在構建演示時解決它。 – NightOwl888

回答

2

運行我敢肯定,這是由我們的網站地圖配置/設置問題造成的。我們一直依靠我認爲在SiteMap中的bug,它以前保留了路線數據。然而,在第4版中,這是固定的。當我們參觀了其中有一個節點的屬性放在而是行動

性能問題被引起不包含需要保存的路由數據。我認爲試圖找到一個合適的節點/路線,網站地圖提供者似乎有點瘋狂。

我們現在把很多preserveRouteData的整個代碼,並能夠解決問題。我們非常想現在創建動態節點(如我們的整個網站地圖是基於各主要實體,例如,有很多的子節點的人們吊離它們),以避免這種情況。但是我們有問題,因爲我們還想使用屬性來添加子節點到動態節點。參見here

+0

如果您可以組合一個小型演示項目來顯示錯誤配置(以及如何解決此問題)並將其提供下載,我將不勝感激。理想情況下,在這種情況下,MvcSiteMapProvider應該拋出一個異常,告訴你要重新配置什麼,而不是進入無限循環。 – NightOwl888

0

如果您的網站在調試模式下運行,它將導致html helper的性能下降。請注意,這是V3和V4應該有所改善。

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

已知的性能問題和解決性能下降 可能會注意到從Visual Studio中的HtmlHelper職能工作。 這是因爲在調試期間,ASP.NET MVC內部沒有發生有關視圖呈現的內部緩存。這個問題的解決是運行在釋放模式 應用或改變的Web.config下 釋放模式運行:

<compilation debug="false"> 
+0

測試版本。此外,我們還對路徑旁路視圖引擎進行了編碼,從而提高了視圖查找的性能 – GraemeMiller