Examining ASP.NET 2.0's Site Navigation - Part 4
By Scott Mitchell
A Multipart Series on ASP.NET 2.0's Site Navigation |
---|
This article is one in a series of articles on ASP.NET 2.0's site navigation functionality.
SiteMap class;
includes a thorough discussion of the SiteMapPath (breadcrumb) control. |
Introduction
The goal of ASP.NET's site navigation feature is to allow a developer to specify a site map that describes his website's logical structure. A site map is constructed of an arbitrary number of hierarchically-related site map nodes, which typical contain a name and URL. The site navigation API, which is available in the .NET Framework via the
SiteMap
class, has properties for accessing the root node in the site map as well as the "current" node (where the "current" node is
the node whose URL matches the URL the visitor is currently on). As discussed in
Part 2 of this article series, the data from the site
map can be accessed programmatically or through the navigation Web controls (the SiteMapPath, TreeView, and Menu controls).
The site navigation features are implemented using the provider
model, which provides a standard API (the SiteMap
class) but allows developers to plug in their own
implementation of the API at runtime. ASP.NET 2.0 ships with a single default implementation,
XmlSiteMapProvider
,
with which the developer can define the site map through an XML file (Web.sitemap
); Part 1
of this article series looked at defining this XML file. However, our site's structure might already be specified by existing
database data, or perhaps by the folders and files that makeup our website. Rather than having to mirror the database or
file system structure in a Web.sitemap
file, we can create a custom provider that exposes the database
or file system information as a site map.
Thanks to the provider model we can provide a custom implementation of the site navigation subsystem, but one that still
is accessible through the SiteMap
class. In essence, with a custom provider the SiteMap
class
and navigation Web controls will work exactly as they did with the XmlSiteMapProvider
. The only difference will
be that the site map information will be culled from our own custom logic, be it from a database, a Web service, the file system,
or from whatever data store our application may require. In this article we'll look at how to create a custom site
navigation provider and build a file system-based custom provider from the ground-up. Read on to learn more!
The Job of a Custom Site Map Provider
In English, the responsibility of a site map provider is to return the site map upon request, where a site map is a collection of hierarchically-related site map nodes. More concretely, each site map node is implemented in the .NET Framework as an instance of the
SiteMapNode
class. A custom site map provider, then, will need to perform the following:
- Get the site navigation information (this may be in a database, based on the file system, etc.)
- Iterate through the site navigation information, creating a
SiteMapNode
for each logical section - Adding nodes to the site map, forming a hierarchy of site map nodes, keeping in mind that there can be only one root node.
Web.config
file, the custom site map provider's Initialize()
method is called and the attribute names and value specified
for the provider in Web.config
are passed along. The custom provider's Initialize()
method can
read these attribute names and values and save them as needed. For example, a site map provider that accessed
site structure information from a database would need a connection string specified in the provider markup in the
Web.config
file. In the Initialize()
method this connection string value could be saved to
a member variable in the class, so later, when constructing the site map, it could be used to connect to the database.
Extending the StaticSiteMapProvider
Class
The .NET Framework offers a
StaticSiteMapProvider
class that contains the core functionality needed for a custom
site map provider. To build our own site map provider, then, we merely need to create a class that inherits the StaticSiteMapProvider
class
and provides an implementation for the following two methods:
GetRootNodeCore()
- returns the root of the site map.BuildSiteMap()
- constructs the site map and returns the root node.
Initialize()
method for any custom providers that need to read custom settings from Web.config
.
Building a File System-Based Custom Site Map Provider
For simple websites that are only composed of a handful of static pages, web designers typically structure the file system to mimic the navigational structure of the website. For example, the demo website examined in the previous three parts of this article series used the following navigational structure:

The files and folder structure in the website mimicked this logical structure. There was a Books
folder with
pages named Novels.aspx
, Romance.aspx
, History.aspx
and so on. Similarly, there were
folders named Electronics
, DVDs
, and Computers
. Rather than having to mirror this
file system information in the Web.sitemap
file (and remember to update it when adding new pages, renaming directories
or files, or removing pages altogether), we could create a custom site map provider that based the site map's content
on the file system structure.
I've created such a custom site map provider and named it FileSystemSiteMapProvider
. This class, along with an
ASP.NET 2.0 website that uses the custom provider, is available for download at the end of this article.
The Practicality of a File System-Based Site Map Provider |
---|
Does a file system-based site map provider make sense for real-world web applications? It depends. For small websites
that have a tight affinity between the site's file system layout and its navigational structure, such a site map provider
makes sense. For data-driven sites that have pages whose content is dynamically generated based on parameters like querystring
values, or for sites where there's no connection between the file system layout and navigational structure, the file system
site map provider is ill-fitted.
For data-driven sites you'll likely want to use a custom site map provider that builds the site map based on the contents from a database. While we won't look at building such a provider in this article, refer to Jeff Prosise's article The SQL Site Map Provider You've Been Waiting For for information on building a site map provider that hits against a SQL Server database. I also have an implementation of a SQL site map provider available in this example. |
The FileSystemSiteMapProvider
Provider's Properties
By default the
FileSystemSiteMapProvider
provider enumerates every ASP.NET page and folder in the web application,
excluding only the Bin
folder and folders that being with the App_
prefix. However, there may be some
ASP.NET pages or folders that you do not want to appear in the site map. The FileSystemSiteMapProvider
provider offers two optional properties that can be specified to achieve this aim:
ExcludeFileList
- a list of files that should not be included in the site map. Each file must be fully qualified. For example, in the diagram shown above, imagine that we wanted to exclude theNovels.aspx
page from the site map. We'd need to add~/Books/Novels.aspx
toExcludeFileList
.ExcludeFoldeerList
- a list of folders that should not be included in the site map. If a file is excluded, all of its files and subfolders are excluded as well. Likewise, the folder path must be fully specified, like~/DVDs/
.
ExcludeFileList
and ExcludeFoldeerList
properties are StringDictionary
objects;
furthermore, they are both Protected
, meaning that you can't work with this property directly from your ASP.NET page.
Instead, you'll need to use the appropriate helper methods for adding, removing, and enumerating these properties. (See
the SiteMap_Info.aspx
page in the download for an illustration of using these helper methods.) However, you can
set these properties to their initial values through the Web.config
file, as we'll see shortly.
In addition to these two exclusion properties, FileSystemSiteMapProvider
contains four other properties:
RootUrl
- the fully qualified path to the page that should serve as the root node in the site map. If this value isn't specified, the default value is~/Default.aspx
.RootTitle
- the title to display for the rootSiteMapNode
. Defaults to "Home"UseDefaultPageAsFolderUrl
- a Boolean property that indicates whether or not to use theDefaultPage
as the folder URL. If this property is True (the default) then eachSiteMapNode
created in the site map for a folder has itsUrl
property set to~/FOLDER/DefaultPage.aspx
(if the file exists). Furthermore, the file~/FOLDER/DefaultPage.aspx
(if it exists) is not added as a child of the folder.
To understand this property's implications, refer to the diagram shown earlier of the site's logical structure. Imagine that the site has a~/Books/Default.aspx
page. If you want the Books node in the site map to be clickable and take the user to~/Books/Default.aspx
, leave this property set to True. If, however, you don't want the Books node to be clickable, and instead want to add a fourth child of the Books node that sends the user to~/Books/Default.aspx
, then set this property to False.DefaultPageName
- the file name for the page that is the default document. Defaults toDefault.aspx
.
Building the Site Map
A site map provider that extends
StaticSiteMapProvider
(like ours does) must implement the BuildSiteMap()
method, whose purpose is to construct the site map (if needed) and return the root SiteMapNode
. Since this method
can be called multiple times per page request (since a page might have multiple navigation Web controls), it behooves us to
utilize caching as much as possible. That is, we don't want to have to hit the file system and rebuild the site map every single
time this method is invoked. Rather, we want to build it once and cache this tree until there's some file system-level change.
Caching the tree is simple enough - we just create a _root
SiteMapNode
variable at the class level and
assign this variable to the root of the SiteMap. This approach builds the site map just once, and then caches it until
the web application is restarted or the FileSystemSiteMapProvider
class is edited. This caching is too aggressive,
however, because if new ASP.NET pages are added to the file system, or existing ones deleted, the site map will not pick up
these changes.
To alleviate this problem I utilize the CacheDependency
object, which can be used to monitor a set of files and/or
folders. Specifically, I point it to the root folder (~/
) and whenever BuildSiteMap()
is called I
check to see if the file system has been changed since BuildSiteMap()
was last called. If not, then I simply
return the _root
reference, since there's no need to rebuild the site map. If, however, there has been a change,
I recreate the dependency, clear out the site map, and rebuild it.
The BuildSiteMap()
method and the recursive BuildSiteMapFromFileSystem()
shown below are the workhorses
for creating the site map. I've left off a couple of the helper methods (CreateFileNode()
and CreateFolderNode()
,
for example), for brevity. These methods can be explored by downloading the complete code at the end of this article.
Public Overrides Function BuildSiteMap() As System.Web.SiteMapNode
|
Extending the FileSystemSiteMapProvider
Provider
The
FileSystemSiteMapProvider
implementation provided here uses a very simply algorithm for determining the title
to display for the SiteMapNode
s for the files and folders - it just uses the file or folder name, replacing
underscores (_
) with spaces. However, you may want to base the file name on the value in the <title>
element (if it exists), or based on some custom <meta>
tag or some other criteria. You can easily add this
functionality by extending the FileSystemSiteMapProvider
class and overridding the GetFileTitle()
and GetFolderTitle()
methods. These two methods return the title used by the SiteMapNode
for a specified
file or folder path.
Plugging the FileSystemSiteMapProvider
Provider Into Your Website
With the
FileSystemSiteMapProvider
custom provider complete, the last step is to plug it into your website and
to start using it in place of the default XmlSiteMapProvider
. To accomplish this, add the following markup to
your application's Web.config
file:
|
All of the attributes in the <add>
element are optional (except for name
and type
).
Refer to the "The FileSystemSiteMapProvider
Provider's Properties" section earlier in this article for a rundown
on this custom provider's properties and their default values.
Conclusion
One of the key benefits of ASP.NET 2.0 is its extensibility. With its myriad of subsystems built atop the provider model, ASP.NET 2.0 provides a standardized API that permits custom implementation. In this article we saw how to utilize the provide model and site navigation, creating a custom site map provider that is based on the file system structure. Such a site map provider will likely prove useful for those developing small, relatively static websites whose file system structure closely matches the site's navigational structure.
Happy Programming!
Attachments
A Multipart Series on ASP.NET 2.0's Site Navigation |
---|
This article is one in a series of articles on ASP.NET 2.0's site navigation functionality.
SiteMap class;
includes a thorough discussion of the SiteMapPath (breadcrumb) control. |