根据用户角色将XML站点地图加载到MvcSiteMapProvider中

我已经安装了MvcSiteMapProvider的v4,现在我想动态加载站点地图。 我的需求很简单 – 根据当前登录的用户角色在每个页面请求上加载XML站点地图,例如。 AdminSiteMap.xml和UserSiteMap.xml

看来这可以做到:

  • 使用带有2个站点地图的asp.net MVCSiteMapProvider v4
  • https://github.com/maartenba/MvcSiteMapProvider/wiki/Multiple-Sitemaps-in-One-Application

所以基本上你需要使用DI来实现这个目标( 矫枉过正恕我直言 )。 没有 DI可以做到这一点吗?

因此,当我使用ASP Boilerplate( http://www.aspnetboilerplate.com/ )时,我将Castle Windsor作为我的DI。

所以我通过NuGet安装了“MvcSiteMapProvider MVC5 Windsordependency injection配置”。 但是现在当我运行应用程序时,我收到以下错误:

SiteMapLoader尚未初始化。

Check the 'MvcSiteMapProvider_UseExternalDIContainer' setting in the AppSettings section of web.config. If the setting is set to 'false', you will need to call the MvcSiteMapProvider.DI.Composer.Compose() method at the end of Application_Start in the Global.asax file. Alternatively, if you are using .NET 4.0 or higher you can install the MvcSiteMapProvider.MVCx NuGet package corresponding to your MVC version. If the setting is set to 'true', you must set the SiteMaps.Loader property during Application_Start in Global.asax to an instance of the built-in SiteMapLoader type or a custom ISiteMapLoader instance. This can be achieved most easily by using your external DI container. 

我没有更改默认配置,并确认在公共类MvcSiteMapProviderInstaller:IWindsorInstaller中调用Install()方法,因为它在那里遇到断点。

所以我在这里缺少什么才能完成这项工作。 请记住,我要做的就是根据每个请求的登录用户加载SiteMap。

****更新****

虽然它可能不太优雅,但它并不需要实现DI容器所提出的大量代码。 在@ Using Multiple MvcSiteMaps上查看viggity的答案(关于第4个)

首先,每个用户可以使用1个SiteMap,但不能很好地扩展 – 实际上它几乎违背了制作站点地图的目的。 我不推荐这种方法,除非您确定您的网站不会有超过几十个并发用户,您在网站上的页面少于几百页,并且您在服务器上有额外的内存空间。

根据哪个用户登录,有更多可伸缩选项可使节点可见/不可见。

  • 使用安全修整 。 启用后,只需使用AuthorizeAttribute正确配置MVC安全性,即可自动运行。 AuthorizeAttribute完全支持角色。 如果需要,您还可以inheritanceAuthorizeAttribute以添加自定义安全逻辑。
  • 使用自定义可见性提供程序根据自定义条件控制每个节点是可见还是不可见。
  • 自定义内置HTML帮助程序(通过更改/Views/Shared/DisplayTemplates/文件夹中的模板)或构建自定义HTML帮助程序,以便除了来自SiteMap实例的链接之外,还为每个用户的每个请求动态加载链接。

这些方法都不需要外部DI。

建议的方法是将所有节点加载到每个用户可能访问的SiteMap中,然后使用安全修整使UI上的当前用户的节点不可见。 您可以在数据更改时强制重新加载缓存,方法是使用SiteMapCacheReleaseAttribute在将UI添加到数据源后立即在UI上显示动态节点 。


有了这些知识,如果您仍想继续沿着目前的路径前进,那么您已经安装了错误的NuGet包。 dependency injection的工作方式是在项目中只需要1个组合根(即WindsorContainer的一个实例)。 由于项目中已有组合根,因此必须为Windsor安装 MvcSiteMapProvider 模块的软件包,然后通过添加几行代码手动将模块添加到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(); // Check all configured .sitemap files to ensure they follow the XSD for MvcSiteMapProvider (optional) var validator = container.Resolve(); validator.ValidateXml(HostingEnvironment.MapPath("~/Mvc.sitemap")); // Register the Sitemaps routes for search engines (optional) XmlSiteMapController.RegisterRoutes(RouteTable.Routes); 

如果您确保项目范围内只有一个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().ImplementedBy(); 

剩下的唯一事情是使用动态节点提供程序或ISiteMapNodeProvider的实现来动态地为每个用户提供节点。 如果按上述方式进行设置,则可以通过SiteMap.CacheKey属性获取用户名。

 public class SomeDynamicNodeProvider : DynamicNodeProviderBase { public override IEnumerable 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.  // Use a "dummy" node for each dynamic node provider. This node won't be in the SiteMap.