ASP.NET 2.0 and Site Maps

Thursday, July 8, 2004
I hereby interrupt the “week of the malcontent” with the “evening of possible enlightenment”.

The provider design pattern in ASP.NET 2.0 is sweet. I’ve been toying around in the SiteMapProvider area since the first CTP. There is an XmlSiteMapProvider which let’s you describe the navigation and layout of your web site in an XML file like so:

<?xml version="1.0" encoding="utf-8" ?>
<siteMap>
  <siteMapNode title="Home" url="Default.aspx" >
    <siteMapNode title="Products" url="Products.aspx">
      <siteMapNode title="Seafood" url="Seafood.aspx"/>
      <siteMapNode title="Produce" url="Produce.aspx"/>
    </siteMapNode>
    <siteMapNode title="Contact" url="Contact.aspx">
      <siteMapNode title="Email Us" url="Email.aspx"/>
      <siteMapNode title="Phone List" url="Phones.aspx" />
    </siteMapNode>
  </siteMapNode>
</siteMap>

Without writing a line of code (just some drag and drop operations), you can give all the pages in your site a tree view of the site hierarchy and a bread crumb control:

What if your site navigation comes from a database table?

NodeID URL           Name     ParentNodeID 
------ ------------- -------- ------------ 
1      Default.aspx  Home     0
2      Products.aspx Products 1
3      Seafood.aspx  Seafood  2
4      Produce.aspx  Produce  2
5      Contact.aspx  Contact  1
6      Email.aspx    Email    5
7      Phones.aspx   Phone    5

All you need to do is derive from the abstract class SiteMapProvider and override a handful of methods, like GetParentNode and GetChildNodes. These methods can be straightforward to implement with a few pre-built collections of type Dictionary<string, SiteMapNode>. SiteMapNode objects represent the nodes in the site map, while a Dictionary is one of the exciting new classes from the System.Collections.Generic namespace, which you can use to build strongly typed collections.

One you have some code querying SQL Server and implementing the SiteMapProvider methods, you just need to tell the runtime about your new provider via a config file:

<siteMap defaultProvider="MySqlSiteMapProvider" enabled="true"> 
  <providers> 
    <add name="MySqlSiteMapProvider" type="SqlSiteMapProvider"
         connectionStringKey="ConnectionString"/> 
  </providers> 
</siteMap> 

You can have multiple providers for a site. If half of the site navigation information comes from XML and the other half from the database, that’s quite possible. It will be interesting to see what other providers come out. I'm sure SQL, and File System providers will be in demand.


Comments
Sharad Kumar Sunday, September 19, 2004
Hi Scott,
<br>
<br>Sitemaps are undoubtedly cool. I've tried to find info on merging of two sitemaps, but found none in SDK or MSDN. Even when we add another Provider, only one at a time is active/default. How to setup hierarchy within them? Any direction shall be outstanding. Thanks in advance!
<br>
<br>--
<br>Sharad
Scott Allen Tuesday, September 21, 2004
Hi Sharad:
<br>
<br>I'll have to give this a try over the next couple weeks and see what I come up with. I have a provider written for SQL Server, so I'll try combining the Xml and the SQL providers.
Randall Thursday, November 11, 2004
Hi Scott. Did you ever get the SQL SiteMapProvider working? I was wondering If I might peek at the code?
<br>
<br>thx, -R
Scott Allen Thursday, November 11, 2004
Yes I did, send me an email (scott AT odetocode.com) or ping me on MSN IM (bitmask AT yahoo.com) and I'll hand it over.
wyx2000 Saturday, December 18, 2004
I am not sure if I misunderstand something here, I create a HierarchicalDataSource to read sitemap from my db(now read from my another class since I need the sitemap in another place too), But I am not sure if it is the sitemapprovider you are talking about here. I think not, does the provider you are talking here can replace the embed sitemapprovider? that means you can just put a SiteMapDataSource on your page, and the sitemap is from your provider? and when you call SiteMap.CurrentNode, the property is actually from your provider? if it, great then, can I have some articles about it?
wyx2000 Saturday, December 18, 2004
Sorry, leave my email, hope someone can point me the right direction.
<br>
<br>wyx1999@hotmail.com
automation Wednesday, January 5, 2005
Here we are 2005, great tool being created, and there is no autocreation of a sitemap?
<br>
<br>Also - where is the browse to the sitemap?
<br>
<br>Just seems half baked by Microsoft.
Sky Sigal Monday, March 7, 2005
Hello Scott:
<br>
<br>I see that you were going to try to combine two different site.maps into one:
<br>
<br>&quot;I have a provider written for SQL Server, so I'll try combining the Xml and the SQL providers.&quot;
<br>
<br>I was just wondering if you succeeded at this, and care to share any insights, as as I'm getting no successes here trying to mix an xml site map mixed with a file directory based site map provider :-(
<br>
<br>
<br>
<br>Thanks so much!
<br>
rap Saturday, May 7, 2005
I have test it and don't run :(
Ali Tuesday, August 9, 2005
I'm Struggling with the concept of SiteMapProvider and the implementation of the SiteMap Path control !

What's the easiest and basic way to implement it and provide a ready-made Provider for it ?

"Any Clues ! Links ! are appreciated " !
scott Tuesday, August 9, 2005
I know there is not much out there, Ali.

You can look at the sample "SimpleTextSiteMapProvider" provided in the docs: http://msdn2.microsoft.com/library/e52te4hd(en-us,vs.80).aspx

Other than that, the way I learned the most about the SiteMapProvider was by using Reflector to view the source code in System.Web.dll : http://www.aisto.com/roeder/dotnet/
George Cosoveanu Tuesday, November 8, 2005
Hi Scott,

I managed to implement a SQL server SiteMap provider and it is working.
However, when I am making a change in SQL Server in the SiteMap table, it is not reflected on the page when I do a refresh (I even set SQL Cache invalidation on my SQL Server 2000 database). Only when I close and re-open the Visual Web Developer 2005 the change appears on the page.
Any idea how to fix this?

Thanks,
George
scott Friday, November 11, 2005
Hi George:

Does this happen on a web page or in the designer? If it is the desginer I'm not sure what to do - I'll have to experiment.
sloan Saturday, December 10, 2005


I have seem to found a small issue with the nesting.

My default Web.sitemap file:

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="schemas.microsoft.com/.../SiteMap-File-1.0" >
<siteMapNode title="Everybody Stuff" url="thisDoesntReallyExistButCausesAnIssueIfIRemoveIt.aspx">
<siteMapNode url="everybody1.aspx" title="Everybody1" description="MyWeb1" />
<siteMapNode url="everybody2.aspx" title="Everybody2" description="MyWeb2" />
</siteMapNode>
</siteMap>



Ok, then I have another sitemap file.

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="schemas.microsoft.com/.../SiteMap-File-1.0" >
<siteMapNode title="hidden root" description="hidden root">

<siteMapNode url="~/UserData/UserInfoViewAll.aspx" title="View All Users" description="View Users" />
<siteMapNode url="~/UserData/UserInfoManage.aspx" title="Manage Users" description="Manager Users in the System" />

<siteMapNode siteMapFile="~/web.sitemap" />
</siteMapNode>
</siteMap>



Notice that I nest "Web.sitemap" into the second file.


If I leave the "url" attribute for "Everybody Stuff", the menu displays as I would predict.

If I remove this xml attribute
( url="thisDoesntReallyExistButCausesAnIssueIfIRemoveIt.aspx" )
(as in , I take it out completely),
the resultant menu doesn't show any of the "everybody stuff" items.

I've tried several things to get it show up, to no avail.

..

FYI, I also implemented a custom XmlSiteMapProvider. This checks my custom IPrincipal object to see if the use is or isn't in certain roles. I'll post that code, and the corresponding web.config also.

public class GranadaCoderSecureMapProvider : XmlSiteMapProvider
{
public GranadaCoderSecureMapProvider()
{

}


public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)
{
//return base.IsAccessibleToUser(context, node);
string currentURL = node.Url;


if (currentURL.Length > 0)
{
//custom functionality here

// here is a simple substitution
return true;

}
return true;



}

}

And web.config section, dealing with this.

<system.web>


<siteMap defaultProvider="XmlSiteMapProvider">
<providers>
<add name="XmlSiteMapProvider" type="System.Web.XmlSiteMapProvider" siteMapFile="~/Web.sitemap" securityTrimmingEnabled="true" />
<add name ="Company1SiteMap" type ="GranadaCoderSecureMapProvider" siteMapFile="~/Sitemaps/confirmedUsers.sitemap" securityTrimmingEnabled="true" />
</providers>
</siteMap>


So, I'm trying to figure out if/why I'm not getting the expected results.

If anyone sees something screwball in my setup, let me know.
I guess this is kinda new territory (Dec 2005), and there isn't alot on the web about this stuff yet, especially the nesting.

Thanks.
scott Saturday, December 10, 2005
Sloan: I have not tried nesting as yet, I'll have to take a look at this.
sloan Tuesday, December 13, 2005
Ok, thanks. I'll check back in a few days.
..
sloan Monday, January 9, 2006

Scott, I was hoping now that New Year has past you might be able to check the issue I reported.
Thanks.

Ryan Tuesday, January 10, 2006
I have converted the MSDN C# SQLSiteMapProvider (msdn.microsoft.com/.../default.asp) to VB.NET if anyone is interested in checking it out.

It works fine, only trouble I am having now is that it caches the sitemap for waaaaaay to long.
docluv Friday, January 20, 2006
Ryan,

I am trying to get a VB version of the SQL Provider working from that article right now. It only shows the first node and its children, no siblings.
I have a thread on asp.net, http://forums.asp.net/1173914/ShowPost.aspx
Any help you could provide would be great.
scott Sunday, January 22, 2006
Have you considered pushing all of your sibling nodes underneath a top level node? The site map data source controls can always be configured to hide the top node from navigation menus and such.
Leo Thursday, February 16, 2006
Hi Scott,

I tried SqlSiteMapProvider in Visual Studio 2005 and tested direclly in design mode, i found something strange, that is,
I created two roles to get different site map item, when the first role login and get the right top level and sub-level menus, then logout and re-login with the other role, the sub-level menus will be cached the same as the first role's. If I run again with debug point in code, it will get the correct sub-level menus, I had clear cache stuffs in browser, where is the issue comes from ?

Leo
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!