首先,每个用户 1 个站点地图是可能的,但不会很好地扩展 - 事实上,它几乎违背了制作 站点 地图的目的。我不会推荐这种方法,除非您确定您的站点不会有超过几十个同时用户,站点上的页面少于几百个,并且服务器上有大量可用的额外内存。
有更多可扩展的选项可以根据登录的用户使节点可见/不可见。
这些方法都不需要外部 DI。
推荐的方法是将所有节点加载到每个用户可能访问的站点地图中,然后使用安全修整使当前用户的节点在 UI 上不可见。您可以在数据更改时强制重新加载缓存,方法是使用 SiteMapCacheReleaseAttribute 使 dynamic nodes 在将它们添加到数据源后立即在 UI 上可见。
有了这些知识,如果您仍想继续当前的路径,则说明您安装了错误的 NuGet 包。依赖注入的工作方式是您的项目中恰好需要 1 个组合根(即 WindsorContainer 的一个实例)。由于您的项目中已经有一个组合根,您必须为 Windsor 安装 MvcSiteMapProvider modules only 包,然后通过添加几行代码手动将该模块添加到您的 Windsor 配置中。您可以通过在包管理器控制台中运行此命令来降级到仅模块包:
PM> Uninstall-Package MvcSiteMapProvider.MVC5.DI.Windsor
然后,在您的项目中搜索 new WindsorContainer() 的声明位置,并将 MvcSiteMapProvider 模块添加到您的 DI 配置中。
// Create the DI container (typically part of your DI setup already)
var container = new WindsorContainer();
// Your existing DI configuration should typically be here...
// Setup configuration of DI
container.Install(new MvcSiteMapProviderInstaller()); // Required
container.Install(new MvcInstaller()); // Required by MVC. Typically already part of your setup (double check the contents of the module).
// Setup global sitemap loader (required)
MvcSiteMapProvider.SiteMaps.Loader = container.Resolve<ISiteMapLoader>();
// Check all configured .sitemap files to ensure they follow the XSD for MvcSiteMapProvider (optional)
var validator = container.Resolve<ISiteMapXmlValidator>();
validator.ValidateXml(HostingEnvironment.MapPath("~/Mvc.sitemap"));
// Register the Sitemaps routes for search engines (optional)
XmlSiteMapController.RegisterRoutes(RouteTable.Routes);
如果您确保整个项目范围内只有 1 个 WindsorConntainer 实例并根据需要添加上面的代码,那么您应该有一个有效的 DI 配置。
要为每个用户加载 1 个 SiteMap,您需要创建一个自定义 ISiteMapCacheKeyGenerator,它为每个用户返回不同的字符串。
public class UserSiteMapCacheKeyGenerator
: ISiteMapCacheKeyGenerator
{
public virtual string GenerateKey()
{
var context = HttpContext.Current;
if (context.User.Identity.IsAuthenticated)
{
// Note: the way you retrieve the user name depends on whether you are using
// Windows or Forms authentication
return context.User.Identity.Name;
}
else
{
return "default";
}
}
}
然后通过编辑 /DI/Windsor/Installers/MvcSiteMapProviderInstaller.cs 的 Windsor 模块来注入它。
var excludeTypes = new Type[] {
// Use this array to add types you wish to explicitly exclude from convention-based
// auto-registration. By default all types that either match I[TypeName] = [TypeName] or
// I[TypeName] = [TypeName]Adapter will be automatically wired up as long as they don't
// have the [ExcludeFromAutoRegistrationAttribute].
//
// If you want to override a type that follows the convention, you should add the name
// of either the implementation name or the interface that it inherits to this list and
// add your manual registration code below. This will prevent duplicate registrations
// of the types from occurring.
// Example:
// typeof(SiteMap),
// typeof(SiteMapNodeVisibilityProviderStrategy)
typeof(SiteMapNodeUrlResolver),
typeof(ISiteMapCacheKeyGenerator) // <-- add this line
};
// Code omitted here...
// Add this to the bottom of the module
container.Register(Component.For<ISiteMapCacheKeyGenerator>().ImplementedBy<UserSiteMapCacheKeyGenerator>();
剩下的唯一事情是使用dynamic node providers 或ISiteMapNodeProvider 的实现来动态地为每个用户提供节点。如上设置,可以通过SiteMap.CacheKey属性获取用户名。
public class SomeDynamicNodeProvider : DynamicNodeProviderBase
{
public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
{
// Get the user name
var user = node.SiteMap.CacheKey;
// Entities would be your entity framework context class
// or repository.
using (var entities = new Entities())
{
// Add the nodes for the current user only
foreach (var story in entities.Stories.Where(x => x.User == user)
{
DynamicNode dynamicNode = new DynamicNode();
dynamicNode.Title = story.Title;
// The key of the node that this node will be the child of.
// This works best if you explicitly set the key property/attribute
// of the parent node.
dynamicNode.ParentKey = "Home";
dynamicNode.Key = "Story_" + story.Id;
dynamicNode.Controller = "Story";
dynamicNode.Action = "Details";
// Add the "id" (or any other custom route values)
dynamicNode.RouteValues.Add("id", story.Id);
yield return dynamicNode;
// If you have child nodes to the current node, you can
// nest them here by setting their ParentKey property to
// the same value as the dynamicNode.Key and returning them
// using yield return.
}
}
}
}
最后,将“模板”节点添加到配置中以加载动态节点。
// Set a key explicitly to attach the dynamic nodes to.
// The key property here corresponds to the ParentKey property of the dynamic node.
<mvcSiteMapNode title="Home" controller="Home" action="Index" key="Home">
// Use a "dummy" node for each dynamic node provider. This node won't be in the SiteMap.
<mvcSiteMapNode dynamicNodeProvider="NamespaceName.SomeDynamicNodeProivder, AssemblyName"/>
</mvcSiteMapNode>