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>
残りのノードは、コントローラー アクションの属性を使用して追加されています
リリースモードで実行しています