Click here to Skip to main content
Click here to Skip to main content

Building a dynamic SiteMap in ASP.NET 2.0 for a large website

By , 21 Dec 2006
 

dynamic sitemap rendered

Introduction

Sitemaps and breadcrumbs (SiteMapPath) are useful and easy to implement for a static site with a sitemap file. For dynamic sites, something as simple seems to get much more complicated.

When I started reading about sitemaps for dynamic sites, I found a common approach: generate a static site map for the whole website from, for example, a data source. Re-generate periodically. Use the XmlSiteMapProvider. Cache. The technique is described in this CodeProject article.

This doesn't work for my site at all. I need to provide a site map for a large number of pages, potentially hundreds of thousands. The site is deep and dynamic, and only a small unpredictable set of pages is accessed frequently. There're also two hundred different object types in the back-end database which is not coupled with the user-interface at all. Querying everything once is not trivial. Generating an accurate site map for the whole site would take too long, would be a lot of code, and would definitely take too much memory to cache.

This article demonstrates a simpler and practical solution chosen. You can see it working live on www.foodcandy.com.

Architecture

The core of the architecture is a relatively simple dynamic data provider, SiteMapDataProvider, based on the StaticSiteMapProvider. The provider is used as the default provider throughout the application, and can stack nodes that appear in the site map path. Each document that renders dynamic content stacks itself with the appropriate hierarchy of parent nodes.

Implementation

The SiteMapDataProvider is straightforward. It will create a root node, and can stack a node behind any existing node, creating a path.

public class SiteMapDataProvider : StaticSiteMapProvider
{
 private SiteMapNode mRootNode = null;

 ...

 // create the root node
 public override void Initialize(string name, 
        NameValueCollection attributes)
 {
     base.Initialize(name, attributes);
     mRootNode = new SiteMapNode(this, "Home", 
                 "Default.aspx", "Home");
     AddNode(mRootNode);
 }

 public override SiteMapNode FindSiteMapNode(string rawUrl)
 {
     return base.FindSiteMapNode(rawUrl);
 }

 // stack a node under the root
 public SiteMapNode Stack(string title, string uri)
 {
     return Stack(title, uri, mRootNode);
 }

 // stack a node under any other node
 public SiteMapNode Stack(string title, string uri, 
                    SiteMapNode parentnode)
 {
     lock (this)
     {
         SiteMapNode node = base.FindSiteMapNodeFromKey(uri);

         if (node == null)
         {
             node = new SiteMapNode(this, uri, uri, title);
             node.ParentNode = 
               ((parentnode == null) ? mRootNode : parentnode);
             AddNode(node);
         }
         else if (node.Title != title)
         {<BR>             // support renaming documents
             node.Title = title;
         }
  
         return node;
     }
 }
}

I have put the above implementation in a DBlock.SiteMapProvider.dll assembly, and have referenced it in web.config as the default provider.

<?xml version="1.0"?>
<configuration>
 <system.web>
  <siteMap enabled="true" 
            defaultProvider="SiteMapDataProvider"> 
   <providers>
    <add name="SiteMapDataProvider" 
       type="DBlock.SiteMapDataProvider, DBlock.SiteMapDataProvider" />
   </providers>
  </siteMap>
 </system.web>
</configuration>

I've added a simple master page with a SiteMapPath and a default page. This yields a Home (root) site map node on Default.aspx.

Now remember, the goal is to have a dynamic site map. I've added a button that redirects to Default.aspx?id=<guid> for demo purposes. To display the site map accordingly, I've built a linked list of site map nodes, then stacked them into the site map.  Below is the Default.aspx Page_Load and one more helper function in SiteMapDataProvider to do the stack.

string id = Request["id"];
if (!string.IsNullOrEmpty(id))
{
  List<KeyValuePair<string, Uri>> nodes = 
     new List<KeyValuePair<string, Uri>>();
  nodes.Add(new KeyValuePair<string, 
     Uri>("Dynamic Content", new Uri(Request.Url, "Default.aspx?id=")));
  nodes.Add(new KeyValuePair<string, Uri>(Request["id"], Request.Url));
  ((SiteMapDataProvider) SiteMap.Provider).Stack(nodes);
}
public void Stack(List<KeyValuePair<string, Uri>> nodes)
{ 
  SiteMapNode parent = RootNode; 
  foreach (KeyValuePair<string, Uri> node in nodes)
  {
   parent = Stack(node.Key, node.Value.PathAndQuery, parent); 
  } 
} 

Points of Interest

This works very well for pages that retrieve dynamic content. My project typically retrieves objects from the database or cache, and displays them on each page. Adding two-three lines of code to each page generates the complete site map of pages that are actually being hit.

The obvious disadvantage of this technique is that you have to add code in each page to generate the site map, and that you must carefully track the URL of your intermediate nodes (the Default.aspx?id=, with a blank ID, in the example above) to keep things consistent. It would be great to describe the relationship between dynamic pages in some other form that can be verified at compile time. Maybe, a better (yet more complex) approach is to extend the XML provider to allow dynamic binding in nodes?

You may also want to limit the size of the site map for large sites to avoid consuming too much memory. Simply clear the site map once it has reached your size limit.

History

  • 2006/12/16: Initial release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

dB.
Team Leader Application Security Inc., www.appsecinc.com
United States United States
Member
Daniel Doubrovkine has been in software engineering for twelve years and is currently development manager at Application Security Inc. in New York City. He has been involved in many software ventures, including Xo3 and Vestris Inc, was a development lead at Microsoft Corp. in Redmond, and director of Engineering at Visible Path Corp. in New York City. Daniel also builds and runs a foodie website, http://www.foodcandy.com.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionDBLock.SiteMapdataProvider - Need function parametersmemberMember 796913914 Feb '13 - 7:00 
I'd like to use your code, but I can't figure out the parameters to pass in!
 
DBlock.SiteMapDataProvider smp = new SiteMapDataProvider();
for example:
 
Initialize("Root", ?);
I know the Initialize function wants a name and an attribut, but what attribute does it want?
I would appreciate if you could provide function paramter information. Sample code using the calls would be great. Thanks, Bob Barbara
GeneralMy vote of 5memberPritesh Aryan31 Jul '12 - 0:25 
nice and help full one...thanks for posting....
GeneralMy vote of 1memberdanialram10 Apr '11 - 20:24 
nothing is der
GeneralRegenerate SitemapmemberSherwin Valdez6 Mar '11 - 19:34 
Hello I have tried to redirect to another page. But it seems that I can;t not populate the sitemap using the same code Frown | :( Default.aspx is ok. But with another page the sitemap is just empty.
 
List<KeyValuePair<string, Uri>> nodes = new List<KeyValuePair<string, Uri>>();
nodes.Add(new KeyValuePair<string, Uri>("Dynamic Content", new Uri(Request.Url, "Default.aspx?id=")));

((SiteMapDataProvider) SiteMap.Provider).Stack(nodes);
 
Do I need to include some code just to populate the sitemap from master page?
GeneralGenerate a SiteMapmemberolehaugen7 Feb '11 - 12:38 
For IIS site(s), you can use the IIS extension module from redflag.co , an added benefit is that this extension module will also show you statistics.
The extension module stores the urls on disk, and it has an open source web app (c# .net 2.0) that shows the data, including examples of how to use. www.redflag.co
QuestionCan I just use the source code in other project rather than using the dllmemberhon1234564 Jan '10 - 18:09 
Dear Db,
I want to trace the source code of SiteMapProvider, how can I just
 
embed the SitemapProvider source code in my project. Can I just change
 
something in web.config and use the source code? Thanks.
AnswerRe: Can I just use the source code in other project rather than using the dllmemberdB.7 Jan '10 - 0:31 
Just copy the source code into your project.
 

QuestionHow to create sitemap using your code with url rewritingmemberRavenet26 Dec '09 - 17:28 
Hi Expert,
You are articles is nice, but you have to explore how we can do this url rewriting, because of when you are come to on the page its original url, so we can't add url on page level.
 
So please give your idea to create dynamic sitemap with url rewriting.
 
thank you
 
RRave
MCTS,MCPD
All experts are welcome to http://codegain.com
 

 

AnswerRe: How to create sitemap using your code with url rewritingmemberdB.7 Jan '10 - 0:34 
Your page needs to have knowledge of what the url should look like, so you construct the site map that has URLs before the rewrite.
 

GeneralRe: How to create sitemap using your code with url rewritingmemberRavenet7 Jan '10 - 2:33 
Hi,
 
Can you provide sample to your words
 
thank you
 
RRave
MCTS,MCPD
All experts are welcome to http://codegain.com
 

 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 21 Dec 2006
Article Copyright 2006 by dB.
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid