如何为每个MVC区域制作1个SiteMap并同时使用MvcSiteMapNodeAttribute?
答案 0 :(得分:0)
请查看this answer以获取有关使用区域设置MvcSiteMapProvider的帮助。必须使用正确的约定配置路由,否则它将无法正常工作。
但是,仅此一项并不能满足此要求,因为没有默认假设您希望每个区域拥有不同的SiteMap。
内部DI容器的行为假设每个域名将有1个SiteMap,并且应用程序中的所有SiteMaps都将使用相同的配置构建。除非您使用外部DI容器并按照Multiple SiteMaps in One Application中的说明覆盖它,否则无法更改此行为。
您可以继续使用内部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”,这样你的区域节点就不会受到非区域节点可见性的影响。
注入基于区域的自定义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")]
您可以将每个区域放在一个单独的程序集中,并将其配置为仅扫描[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>();