I have installed v4 of MvcSiteMapProvider and now I want to load a sitemap dynamically. My needs are simple - load an XML sitemap on each page request based on the currently logged in user role eg. AdminSiteMap.xml and UserSiteMap.xml
It appears that this can be done:
- Using asp.net MVCSiteMapProvider v4 with 2 sitemap
- https://github.com/maartenba/MvcSiteMapProvider/wiki/Multiple-Sitemaps-in-One-Application
So basically you need to use a DI to achieve this (overkill IMHO). Any chance this can be done without a DI?
So as I am using ASP Boilerplate (http://www.aspnetboilerplate.com/) I have Castle Windsor as my DI.
So I installed "MvcSiteMapProvider MVC5 Windsor Dependency Injection Configuration" via NuGet. However now when I run the app I get the following error:
The SiteMapLoader has not been initialized.
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.
I have not changed the default configuration and have confirmed that the Install() method is being called in public class MvcSiteMapProviderInstaller : IWindsorInstaller as it hits a breakpoint in there.
So what am I missing here to make this work. Remember all I am trying to do is load a SiteMap based on the logged in user on each request.
**** UPDATE ****
While it may not be elegant it did not require a huge amount of code as proposed by implementing a DI container. See viggity's answer (about the 4th one down) at @Using Multiple MvcSiteMaps
First of all, 1 SiteMap per user is possible, but will not scale very well - in fact it pretty much defeats the purpose of making a site map. I wouldn't recommend this approach unless you are certain your site won't have more than a few dozen simultaneous users, you have less than a few hundred pages on the site, and you have gobs of extra memory available on the server.
There are more scalable options to make nodes visible/invisible according to which user is logged in.
/Views/Shared/DisplayTemplates/
folder) or build custom HTML helpers to dynamically load links per request for each user in addition to the links from the SiteMap instance.None of these approaches requires external DI.
The recommended approach is to load all of the nodes into the SiteMap that every user could potentially access, then use security trimming to make the nodes for the current user invisible on the UI. You can force a reload of the cache when data changes by using the SiteMapCacheReleaseAttribute to make dynamic nodes visible on the UI immediately after adding them to your data source.
With that knowledge, if you still would like to proceed down the path you are currently on, you have installed the wrong NuGet package. The way dependency injection works is that you need exactly 1 composition root in your project (that is, one instance of WindsorContainer). Since you already have a composition root in your project, you must install the MvcSiteMapProvider modules only package for Windsor and then manualy add the module to your Windsor configuration by adding a few lines of code. You can downgrade to the modules only package by running this command in Package Manager Console:
Then, search for where
new WindsorContainer()
is declared in your project and add the MvcSiteMapProvider module to your DI configuration.If you ensure there is only 1 instance of WindsorConntainer project-wide and add the code above as appropriate, you should have a working DI configuration.
To load 1 SiteMap per user, you will need to make a custom ISiteMapCacheKeyGenerator that returns a different string for each user.
And inject it by editing the Windsor module at
/DI/Windsor/Installers/MvcSiteMapProviderInstaller.cs
.The only thing remaining is to use dynamic node providers or implementations of ISiteMapNodeProvider to dynamically supply the nodes per user. If you set it up as above, you can get the user name through the SiteMap.CacheKey property.
And finally, add your "template" nodes to your configuration to load the dynamic nodes.