Closed pimtel closed 9 years ago
Adding nodes at runtime is not supported. In v3, it wasn't allowed either, but the code that was supposed to lock the sitemap for thread safety was broken.
Adding nodes at cache expiration (and at first request) can be done by using a dynamic node provider or, if using external DI you also have the option to implement ISiteMapNodeProvider yourself.
If you need nodes to be added immediately, you can use the SiteMapCacheRelease attribute to invalidate the cache immediately after adding them to your data store.
If you need certain nodes to appear under certain circumstances, you can either use security trimming in conjunction with AuthorizeAttribute, use visibility providers, or both. The recommended approach is to add all of the nodes that could potentially be visible to the SiteMap, and then use one or both of the options to hide the nodes that should not be visible for the current context.
Sometimes I need to change (or replace) the parent node according to if condition inside the action. I tried your suggestion but I couldn't modify the property of the parent dynamic node.
Is it the method use to get dynamic node collection and have access to dynamic node parent, right?
var dynamicNodeCollectionCollection = SiteMaps.Current.CurrentNode.GetDynamicNodeCollection();
DynamicNode parentDynamicNode = null;
foreach (var dynamicNode in dynamicNodeCollectionCollection)
if (dynamicNode.Key == "MyParentDynamicNode")
parentDynamicNode = dynamicNode;
// parentDynamicNode properties changes and some implementation code
Thanks Regards
It is a bit unusual to call the SiteMap object directly. You can update some fields of a node that way (for the current request only), but adding nodes to the SiteMap is done with a provider that is loaded on each cache expiration and affects every user on the web site, not by calling SiteMaps.Current
which only affects the current request.
Dynamic nodes are linked to their parent nodes through Key-ParentKey relationships. These are 2 strings that you can override when the SiteMap is constructed. Granted, the SiteMap structure will only change when the cache is invalidated or expires, but if that doesn't happen too often, you should be able to make it work without too much of a drain on performance.
Assuming that you are changing the relationship in a data store, You just need to pull the key and foreign keys out of your data store and put them into the Key and ParentKey properties of the dynamic node. The data store can then be updated to change the relationships, and the SiteMap will be driven by the data store. Keys must be unique within the SiteMap.
public class MyDynamicNodeProvider
: DynamicNodeProviderBase
{
public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node)
{
using (var db = new MyEntities())
{
// Load primary nodes and attach them to an existing node in the SiteMap
// as the parent key.
foreach (var node in db.Nodes.Where(x => x.Primary == true))
{
DynamicNode dynamicNode = new DynamicNode();
dynamicNode.Title = node.Title;
dynamicNode.Key = node.ID.ToString();
// Map to a node that exists in your SiteMap already
dynamicNode.ParentKey = "Home";
dynamicNode.RouteValues.Add("id", node.ID);
yield return dynamicNode;
// Load second level and map the nodes to each other
foreach (var secondLevelNode in db.Nodes.Where(x => x.Primary == false))
{
dynamicNode = new DynamicNode();
dynamicNode.Title = node.Title;
dynamicNode.Key = node.ID.ToString();
// This would map to either a node in the primary level or
// or a node in the second level, which will control its position
// in the hierarchy.
dynamicNode.ParentKey = node.ForeignKey.ToString();
dynamicNode.RouteValues.Add("id", node.ID);
yield return dynamicNode;
}
}
}
}
}
Primary
is a hypothetical database field that indicates the field should be attached to the home page. This is just to show an example, but you could instead make string fields in the database that map to actual keys in the rest of the SiteMap (note that you would typically set the keys that you want to attach to explicitly for this). You can also use as many tables as you need and either nest them all in a single dynamic node provider or create separate dynamic node providers.
A dynamic node provider requires a definition node to host it. The definition node won't be added to the SiteMap, only the nodes returned from the dynamic node provider will be. You will also need to attach at least one node in the dynamic node provider to a node that exists elsewhere in your SiteMap, as indicated here by linking the first level to the home page. Here is an example in XML, but you could instead host the dynamic node provider in a SiteMapNode attribute on the controller action.
<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0"
xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0 MvcSiteMapSchema.xsd">
<!-- Explicitly give the home page a key -->
<mvcSiteMapNode title="Home" controller="Home" action="Index" key="Home">
<!-- Definition node for the dynamic node provider -->
<mvcSiteMapNode dynamicNodeProvider="MyNamespace.MyDynamicNodeProvider, MyAssembly" />
</mvcSiteMapNode>
</mvcSiteMap>
You can then use the [SiteMapCacheRelease] attribute on the controller actions that change the data in the data store. When the controller action is called the data is updated, the cache is invalidated and the next request will re-load the data from the data store including all of the changes.
Now I understand how it work, your explanation was so clear. Thank you.
Hi there,
I updated the MvcSiteMapProvider from 3.1 to 4, and I'm confuse to how SiteMaps works because I can't use the method AddNode, it throw an exception it tells Sitemaps is readonly. At version 3, I just use it to add new node:
I can't do it anymore, I want to add node programmatically like above.
Thanks Regards