【问题标题】:How do I make MvcSiteMapProvider create 1 SiteMap per Area?如何让 MvcSiteMapProvider 每个区域创建 1 个站点地图?
【发布时间】:2014-04-30 05:31:42
【问题描述】:

如何为每个 MVC 区域制作 1 个 SiteMap 并同时使用 MvcSiteMapNodeAttribute?

【问题讨论】:

    标签: asp.net-mvc mvcsitemapprovider asp.net-mvc-sitemap


    【解决方案1】:

    请查看this answer 以获取有关使用区域设置 MvcSiteMapProvider 的帮助。必须使用正确的约定配置路由,否则将无法正常工作。

    但是,仅此一项并不能满足此要求,因为没有默认假设您希望每个区域拥有不同的 SiteMap。

    内部 DI 容器的行为假定每个域名将有 1 个 SiteMap,并且应用程序中的所有 SiteMap 都将使用相同的配置构建。除非您使用外部 DI 容器并按照 Multiple SiteMaps in One Application 中的说明覆盖它,否则无法更改此行为。

    选项 1

    您可以继续为整个网站使用内部 DI 容器和单个 SiteMap,并且可以创建一个自定义 ISiteMapNodeVisibilityProvider,通过从当前请求中读取区域来隐藏当前区域之外的所有内容。

    public class AreaSiteMapNodeVisibilityProvider
        : SiteMapNodeVisibilityProviderBase
    {
        public AreaSiteMapNodeVisibilityProvider()
        {
            // NOTE: Accept this as a constructor parameter if using external DI and 
            // use a guard clause to ensure it is not null.
            this.mvcContextFactory = new MvcSiteMapProvider.Web.Mvc.MvcContextFactory();
        }
        private readonly MvcSiteMapProvider.Web.Mvc.IMvcContextFactory mvcContextFactory;
    
        #region ISiteMapNodeVisibilityProvider Members
    
        public override bool IsVisible(ISiteMapNode node, IDictionary<string, object> sourceMetadata)
        {
            var requestContext = this.mvcContextFactory.CreateRequestContext();
            var area = requestContext.RouteData.DataTokens["area"];
            var areaName = area == null ? string.Empty : area.ToString();
            return string.Equals(node.Area, areaName, StringComparison.OrdinalIgnoreCase);
        }
    
        #endregion
    }
    

    然后将其设置为默认可见性提供程序。

    <add key="MvcSiteMapProvider_DefaultSiteMapNodeVisibiltyProvider" value="MyNameSpace.AreaSiteMapNodeVisibilityProvider, MyAssemblyName" />
    

    使用外部 DI(显示结构图示例):

    // Visibility Providers
    this.For<ISiteMapNodeVisibilityProviderStrategy>().Use<SiteMapNodeVisibilityProviderStrategy>()
        .Ctor<string>("defaultProviderName").Is("MyNameSpace.AreaSiteMapNodeVisibilityProvider, MyAssemblyName");
    

    请注意,如果您这样做,您仍然需要将您的区域节点嵌套在站点的非区域部分下方,因此它可能不会像您希望的那样运行。您需要确保将管理区域的父键设置为非区域部分中节点的键 - 每个 SiteMap 只能有 1 个根节点。

    另外,如果您走这条路线,请务必将MvcSiteMapProvider_VisibilityAffectsDescendants 设置为“false”,这样您的区域节点就不会受到非区域节点可见性的影响。

    选项 2

    注入一个自定义的基于区域的ISiteMapCacheKeyGenerator,使用[MvcSiteMapNode]属性的SiteMapCacheKey属性来控制节点属于哪个区域。

    public class AreaSiteMapCacheKeyGenerator
        : ISiteMapCacheKeyGenerator
    {
        public AreaSiteMapCacheKeyGenerator(
            IMvcContextFactory mvcContextFactory
            )
        {
            if (mvcContextFactory == null)
                throw new ArgumentNullException("mvcContextFactory");
            this.mvcContextFactory = mvcContextFactory;
        }
    
        protected readonly IMvcContextFactory mvcContextFactory;
    
        #region ISiteMapCacheKeyGenerator Members
    
        public virtual string GenerateKey()
        {
            var requestContext = this.mvcContextFactory.CreateRequestContext();
            var area = requestContext.RouteData.DataTokens["area"];
            return area == null ? "default" : area.ToString();
        }
    
        #endregion
    }
    

    您需要使用外部 DI 注入它(显示结构图示例):

    this.For<ISiteMapCacheKeyGenerator>().Use<AreaSiteMapCacheKeyGenerator>();
    

    然后配置你的 [MvcSiteMapNode] 属性:

    [MvcSiteMapNode(Title = "title", Description = "desc", Key = "root", ParentKey = null, ImageUrl = "fa-home", Order = 0, SiteMapCacheKey = "Admin")]
    [MvcSiteMapNode(Title = "title", Description = "desc", Key = "root", ParentKey = null, ImageUrl = "fa-home", Order = 0, SiteMapCacheKey = "default")]
    

    选项 3

    除了在每个 [MvcSiteMapNode] 属性上设置 SiteMapCacheKey,您可以将每个区域放在单独的程序集中,并将其配置为仅扫描相关区域程序集的 [MvcSiteMapNode] 属性。

    public class AreaSiteMapCacheKeyGenerator
        : ISiteMapCacheKeyGenerator
    {
        public AreaSiteMapCacheKeyGenerator(
            IMvcContextFactory mvcContextFactory
            )
        {
            if (mvcContextFactory == null)
                throw new ArgumentNullException("mvcContextFactory");
            this.mvcContextFactory = mvcContextFactory;
        }
    
        protected readonly IMvcContextFactory mvcContextFactory;
    
        #region ISiteMapCacheKeyGenerator Members
    
        public virtual string GenerateKey()
        {
            var requestContext = this.mvcContextFactory.CreateRequestContext();
            var area = requestContext.RouteData.DataTokens["area"];
            return area == null ? "default" : area.ToString();
        }
    
        #endregion
    }
    
    public class OneToOneSiteMapCacheKeyToBuilderSetMapper
        : ISiteMapCacheKeyToBuilderSetMapper
    {
        public virtual string GetBuilderSetName(string cacheKey)
        {
            return cacheKey;
        }
    }
    

    在外部 DI 模块中(显示结构图示例):

    // Setup the cache
    var cacheDependency = this.For<ICacheDependency>().Use<NullCacheDependency>();
    
    var cacheDetails = this.For<ICacheDetails>().Use<CacheDetails>()
        .Ctor<TimeSpan>("absoluteCacheExpiration").Is(absoluteCacheExpiration)
        .Ctor<TimeSpan>("slidingCacheExpiration").Is(TimeSpan.MinValue)
        .Ctor<ICacheDependency>().Is(cacheDependency);
    
    // Register the ISiteMapNodeProvider instances
    var defaultNodeProvider = this.For<ISiteMapNodeProvider>().Use<ReflectionSiteMapNodeProvider>()
        .Ctor<bool>("includeAssemblies").Is(new string[] { "dllmain" });
    
    var adminNodeProvider = this.For<ISiteMapNodeProvider>().Use<ReflectionSiteMapNodeProvider>()
        .Ctor<bool>("includeAssemblies").Is(new string[] { "dll2" });
        
    // Register the ISiteMapBuilder instances
    var defaultBuilder = this.For<ISiteMapBuilder>().Use<SiteMapBuilder>()
        .Ctor<ISiteMapNodeProvider>().Is(defaultNodeProvider);
    
    var adminBuilder = this.For<ISiteMapBuilder>().Use<SiteMapBuilder>()
        .Ctor<ISiteMapNodeProvider>().Is(adminNodeProvider);
        
    // Register the builder sets
    this.For<ISiteMapBuilderSetStrategy>().Use<SiteMapBuilderSetStrategy>()
        .EnumerableOf<ISiteMapBuilderSet>().Contains(x =>
        {
            // SiteMap builder for the non-area part of the site
            x.Type<SiteMapBuilderSet>()
                .Ctor<string>("instanceName").Is("default")
                .Ctor<bool>("securityTrimmingEnabled").Is(false)
                .Ctor<bool>("enableLocalization").Is(false)
                .Ctor<bool>("visibilityAffectsDescendants").Is(false)
                .Ctor<bool>("useTitleIfDescriptionNotProvided").Is(true)
                .Ctor<ISiteMapBuilder>().Is(defaultBuilder)
                .Ctor<ICacheDetails>().Is(cacheDetails);
    
            // SiteMap builder for the Admin area of the site
            x.Type<SiteMapBuilderSet>()
                .Ctor<string>("instanceName").Is("Admin")
                .Ctor<bool>("securityTrimmingEnabled").Is(false)
                .Ctor<bool>("enableLocalization").Is(false)
                .Ctor<bool>("visibilityAffectsDescendants").Is(false)
                .Ctor<bool>("useTitleIfDescriptionNotProvided").Is(true)
                .Ctor<ISiteMapBuilder>().Is(adminBuilder)
                .Ctor<ICacheDetails>().Is(cacheDetails);
        });
    
    // Register the custom ISiteMapCacheKeyGenerator and ISiteMapCacheKeyToBuilderSetMapper
    this.For<ISiteMapCacheKeyGenerator>().Use<AreaSiteMapCacheKeyGenerator>();
    this.For<ISiteMapCacheKeyToBuilderSetMapper>().Use<OneToOneSiteMapCacheKeyToBuilderSetMapper>();
    

    【讨论】:

    • 太好了,根据stackoverflow.com/a/28439641/923695,如果我们有多个布局,我们可以指定相同的菜单,但告诉它从该区域共享视图文件夹中获取 MVCSitemapLayouts。
    猜你喜欢
    • 2014-11-02
    • 2023-03-20
    • 1970-01-01
    • 2022-12-03
    • 2016-03-29
    • 2013-10-23
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    相关资源
    最近更新 更多