Enhanced XMLSiteMapProvider with support for dynamic nodes.
Hello All,
This might be a long one so bear with me here this might
bore you :).
I have have had the need to be
able to add dynamic nodes to my sitemap, I am using a
sitemap xml file. My first solution, was to create a custom
sitemap provider, which would parse the XML and if it
detected the node I had to insert my dynamic nodes into, it
would do this and then continue.
Fine I thought but not good enough. I wanted a generic solution that could be reused and that would be easy to use. I came up with the idea of extending the sitemap XML file to contain some new nodes. Then create a custom parser that would make use of this. The idea in the end was to add two new elements to the sitemap, the elements being <dynamicNode> and <dynamicNodes>, the idea was that they would call a static method on a class that would either return 1 node or a collection. These would then be injected into the sitemap when it is created.
The beauty here was that you could add dynamic nodes to our sitemap, one other side effect was that you could now nest dynamic nodes in static nodes, and also nest static nodes in either a dynamic node, or a dynamic nodes collection. If you look at the sample project supplied you will see this works quite well. One thing to note is that any static nodes added to either a dynamic node or dynamic nodes collection. It will be added at the end. Which I think is expected behaviour, well for me anyway.
- The new sitemap file -
Here is an example of
what the sitemap file will now look like. Say we have 3
static nodes, and within the static products node, we need
to get products from our database, and add them as children.
You could create an easy site map file like so:
<siteMap
xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"
>
<siteMapNode url="Default.aspx"
title="Home">
<siteMapNode url="About.aspx"
title="About Us" />
<siteMapNode
url="FAQ.aspx" title="FAQ" />
<siteMapNode
url="Product.aspx" title="Products">
<dynamicNodes type="Sample.MenuHelper, Sample"
method="GetProducts" />
</siteMapNode>
</siteMapNode>
</siteMap>
Here you can see that we have added some static nodes,
and within our products node, we have added a dynamicnodes,
node. You can see that this calls the static method
GetProducts on the MenuHelper class. This method will return
a collection of nodes which will be inserted into our static
nodes children. And dynamic insertion of child nodes is now
possible :)... You are also able to have a dynamic node,
which is a single root node, plus you can nest until your
heart is content, which you will see in the sample.
--
MenuHelper --
In this class we would have a
static GetProducts method, this would return a list of XX
objects. These objects are helpers, they allow us to create
a collection of sitemap nodes, so that they can be used in
the provider to build up the sitemap. The node has the
default properties (key,url,title,description) and you can
add custom attributes too. The method would look like so,
you could imagine this would use the database to get
products and build up the list.
public static
List<EnhancedXMLSiteMapProvider.EnhancedSiteMapNode>
GetProducts() {
return new
List<EnhancedXMLSiteMapProvider.EnhancedSiteMapNode>()
{
new
EnhancedXMLSiteMapProvider.EnhancedSiteMapNode() {
Key="Product1.aspx",
Url="Product1.aspx",
Title="Product 1"
}
};
}
-- Web.Config Configuration --
You need to
configure the provider in the web.config. In the sample
project this is done as so:
<siteMap
defaultProvider="EnhancedXMLSiteMapProvider">
<providers>
<add
name="EnhancedXMLSiteMapProvider"
type="EnhancedXMLSiteMapProviderSample.EnhancedXMLSiteMapProvider,
EnhancedXMLSiteMapProviderSample"
siteMapFile="~/Web.sitemap"
cacheDuration="15"/>
</providers>
</siteMap>
I am basically setting it as the default provider, I am
pointing it to the xml sitemap file, and setting the cache
duration (this defaults to 5 if not set). Once this is done
you are ready to run. One thing to note is that the provider
also puts a dependancy on the xml file, so that once changed
it invalidates the sitemap. And also after the time
duration.
One issue is that any dynamic data
does not invalidate the cache, you are able to set a
cacheKey on the provider which is used for caching. Then you
can use code to remove the item. So you backend could remove
the cache item when it needs the menu to change.
-- The Provider --
I am not going to go into full
detail on the provider and show all the code, it is all
included in the sample project so just have a look.
Basically the main workings, just parses the XML file,
depending on the node type will do different things. If it
encounters a dynamicNode or dynamicNodes element it will
grab the type and method attributes, and use reflection to
call these methods and continue parsing the data.
I have tested this with many levels/combinations
of nodes and I have not had any issues yet, mind you I know
this needs neatening up and it is only the efforts of a days
work. I will post updates as I go and any bug fixes to this.
But at the moment I am happy with where it is.
-- Final Word --
The sample project is supplied below, have a play and let me know what you think. Also this is the first time I have ever used the site map provider, so if I have just reinvented the wheel please give me a gentle slap and let me know :).
Sample Project Files
------------------------------------------------------------
EnhancedXMLSiteMapProviderSample.zip
Cheers
Stefan