如何让MvcSiteMapProvider为每个区域创建1个SiteMap?

时间:2014-03-22 19:45:17

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

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

1 个答案:

答案 0 :(得分:0)

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

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

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

选项1

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

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(显示的StructureMap示例):

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

请注意,如果执行此操作,您仍需要将区域节点嵌套在网站的非区域部分下方,因此它可能不会按您的意愿运行。您需要确保将Admin区域的父键设置为非区域部分中的节点的键 - 每个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(显示的StructureMap示例)注入此内容:

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]属性的相关区域程序集。

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模块(显示的StructureMap示例)中:

// 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>();