Question

We want to add dynamic nodes for All People in our system. However we want to attach many child nodes to the dynamic node using attributes

e.g.

[MvcSiteMapNode(Title = "Accidents", ParentKey = "PeopleDetails", Key = "Accidents", PreservedRouteParameters = "personId")]

We have about 40 nodes that should be a child of each person node

E.g.

People > Bob > Absences 
People > Bob > Holidays 
People > Bob > Training

We would like to configure these using Attributes on the Action method e.g Add a Site Map Attribute on Absences Index Action. Absences Index takes the PersonId of the parent nodes as its parameters.

How can we achieve this? I see this article says how to do it in XML Adding non-dynamic children to dynamic nodes. However I can see no way of telling sitemap to add Attribute based nodes to a parent dynamic node. I tried adding an intermediate node under each dynamic in the siteamap using XML giving it a key of PeoplDetails. However its key appears to be swapped for a Guid (I guess to make dynamic nodes children unique)

Any ideas? I really don't want to have to manually configure 40+ child nodes via XML

Was it helpful?

Solution

It is not currently possible to add "static" nodes to dynamic nodes when using MvcSiteMapNodeAttribute. However, you can add "static" nodes (that is, children of data-driven nodes) directly within your IDynamicNodeProvider implementation.

public class PeopleDynamicNodeProvider 
    : DynamicNodeProviderBase 
{ 
    public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node) 
    {
        using (var db = new MyContext())
        {
            // Create a node for each album 
            foreach (var person in db.People) 
            { 
                string personKey = "Person_" + person.Id.ToString();

                DynamicNode dynamicNode = new DynamicNode();
                dynamicNode.Key = personKey;
                dynamicNode.Title = person.Name;
                dynamicNode.ParentKey = "People"; // Attach to a node that is defined somewhere else
                dynamicNode.RouteValues.Add("id", person.Id.ToString());

                yield return dynamicNode;

                DynamicNode staticNode = new DynamicNode(); 
                staticNode.Title = person.Name + " Details"; 
                staticNode.ParentKey = personKey;
                staticNode.Controller = "People";
                staticNode.Action = "Details";
                staticNode.RouteValues.Add("id", person.Id.ToString());

                yield return staticNode;
            }
        }
    } 
}

If you really must define them using MvcSiteMapNodeAttribute, you can divide the above into 2 separate dynamic node providers, and then declare the second one on an attribute.

This will be quite a bit slower because of the additional overhead of multiple calls to the database. You could potentially fix that by request caching your people data so it is shared between providers.

public class PeopleDynamicNodeProvider 
    : DynamicNodeProviderBase 
{ 
    public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node) 
    {
        using (var db = new MyContext())
        {
            // Create a node for each album 
            foreach (var person in db.People) 
            { 
                string personKey = "Person_" + person.Id.ToString();

                DynamicNode dynamicNode = new DynamicNode();
                dynamicNode.Key = personKey;
                dynamicNode.Title = person.Name;
                dynamicNode.ParentKey = "People"; // Attach to a node that is defined somewhere else
                dynamicNode.RouteValues.Add("id", person.Id.ToString());

                yield return dynamicNode;
            }
        }
    } 
}

public class PeopleDetailsDynamicNodeProvider 
    : DynamicNodeProviderBase 
{ 
    public override IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node) 
    {
        using (var db = new MyContext())
        {
            // Create a node for each album 
            foreach (var person in db.People.Where(x => x.PersonCategoryId == node.Key)) 
            { 
                string personKey = "Person_" + person.Id.ToString();

                DynamicNode staticNode = new DynamicNode(); 
                staticNode.Title = person.Name + " Details";
                staticNode.Key = person.PersonCategoryId;
                staticNode.ParentKey = personKey;
                staticNode.Controller = "People";
                staticNode.Action = "Details";
                staticNode.RouteValues.Add("id", person.Id.ToString());

                yield return staticNode;
            }
        }
    } 
}

And your attribute definition would then look something like this:

[MvcSiteMapNode(Title = "Placeholder Title (won't be seen)", Key = "Accidents", PreservedRouteParameters = "personId", DynamicNodeProvider = "MyNamespace.PeopleDetailsDynamicNodeProvider, MyAssembly")]

There is no constraint in a Dynamic Node provider that the number of nodes you produce has to exactly match the number of records in the database - you can create more nodes if you need to depending on your requirements.

Note: You may need to adjust the key/parent key mapping depending on how your data is actually organized.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top