ASP.NET MVC - MvcSiteMapProvider 2.0 is out!

Edit on GitHub

I’m very proud to announce the release of the ASP.NET MVC MvcSiteMapProvider 2.0! I’m also proud that the name of this product now exceeds the average length of Microsoft product names. In this blog post, I will give you a feel of what you can (and can not) do with this ASP.NET-specific SiteMapProvider.

As a warning: if you’ve used version 1 of this library, you will notice that I have not thought of backwards compatibility. A lot of principles have also changed. For good reasons though: this release is a rewrite of the original version with improved features, extensibility and stability.

The example code is all based on the excellent ASP.NET MVC Music Store sample application by Jon Galloway.

Getting the bits

As always, the bits are available on CodePlex: MvcSiteMapProvider 2.0.0
If you prefer to have the full source code, download the example application or check the source code tab on CodePlex.

Introduction

MvcSiteMapProvider is, as the name implies, an ASP.NET MVC SiteMapProvider implementation for the ASP.NET MVC framework. Targeted at ASP.NET MVC 2, it provides sitemap XML functionality and interoperability with the classic ASP.NET sitemap controls, like the SiteMapPath control for rendering breadcrumbs and the Menu control.

Based on areas, controller and action method names rather than hardcoded URL references, sitemap nodes are completely dynamic based on the routing engine used in an application. The dynamic character of ASP.NET MVC is followed in the MvcSiteMapProvider: there are numerous extensibility points that allow you to extend the basic functionality offered.

Registering the provider

After downloading the MvcSiteMapProvider, you will have to add a reference to the assembly in your project. Also, you will have to register the provider in your Web.config file. Add the following code somewhere in the <system.web> section:

[code:c#]

<siteMap defaultProvider="MvcSiteMapProvider" enabled="true">
  <providers>
    <clear />
    <add name="MvcSiteMapProvider"
         type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider"
         siteMapFile="~/Mvc.Sitemap"
         securityTrimmingEnabled="true"
         enableLocalization="true"
         scanAssembliesForSiteMapNodes="true"
         skipAssemblyScanOn=""
         attributesToIgnore="bling"
         nodeKeyGenerator="MvcSiteMapProvider.DefaultNodeKeyGenerator, MvcSiteMapProvider"
         controllerTypeResolver="MvcSiteMapProvider.DefaultControllerTypeResolver, MvcSiteMapProvider"
         actionMethodParameterResolver="MvcSiteMapProvider.DefaultActionMethodParameterResolver, MvcSiteMapProvider"
         aclModule="MvcSiteMapProvider.DefaultAclModule, MvcSiteMapProvider"
         />
  </providers>
</siteMap>

[/code]

The following configuration directives can be specified:

DirectiveRequired?DefaultDescription
siteMapFileNo~/Web.sitemapThe sitemap XML file to use.
securityTrimmingEnabledNofalseUse security trimming? When enabled, nodes that the user can not access will not be displayed in any sitemap control.
enableLocalizationNofalseEnables localization of sitemap nodes.
scanAssembliesForSiteMapNodesNofalseScan assemblies for sitemap nodes defined in code?
skipAssemblyScanOnNo(empty)Comma-separated list of assemblies that should be skipped when scanAssembliesForSiteMapNodes is enabled.
attributesToIgnoreNo(empty)Comma-separated list of attributes defined on a sitemap node that should be ignored by the MvcSiteMapProvider.
nodeKeyGeneratorNoMvcSiteMapProvider.DefaultNodeKeyGenerator, MvcSiteMapProviderClass that will be used to generate sitemap node keys.
controllerTypeResolverNoMvcSiteMapProvider.DefaultControllerTypeResolver, MvcSiteMapProviderClass that will be used to resolve the controller for a specific sitemap node.
actionMethodParameterResolverNoMvcSiteMapProvider.DefaultActionMethodParameterResolver, MvcSiteMapProviderClass that will be used to determine the list of parameters on a sitemap node.
aclModuleNoMvcSiteMapProvider.DefaultAclModule, MvcSiteMapProviderClass that will be used to verify security and access rules for sitemap nodes.

 

Creating a first sitemap

The following is a simple sitemap XML file that can be used with the MvcSiteMapProvider:

[code:c#]

<?xml version="1.0" encoding="utf-8" ?>
<mvcSiteMap xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-2.0" enableLocalization="true">
  <mvcSiteMapNode title="Home" controller="Home" action="Index" changeFrequency="Always" updatePriority="Normal">
    <mvcSiteMapNode title="Browse Store" controller="Store" action="Index" />
    <mvcSiteMapNode title="Checkout" controller="Checkout" />
  </mvcSiteMapNode>
</mvcSiteMap>

[/code]

The following attributes can be given on an XML node element:

AttributeRequired?DefaultDescription
titleYes(empty)The title of the node.
descriptionNo(empty)Description of the node.
areaNo(empty)The MVC area for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy.
controllerYes(empty)The MVC controller for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy.
actionYes(empty)The MVC action method for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy.
keyNo(autogenerated)The unique identifier for the node.
urlNo(autogenerated based on routes)The URL represented by the node.
rolesNo(empty)Comma-separated list of roles allowed to access the node and its child nodes.
resourceKeyNo(empty)Optional resource key.
clickableNoTrueIs the node clickable or just a grouping node?
targetFrameNo(empty)Optional target frame for the node link.
imageUrlNo(empty)Optional image to be shown by supported HtmlHelpers.
lastModifiedDateNo(empty)Last modified date for the node.
changeFrequencyNoUndefinedChange frequency for the node.
updatePriorityNoUndefinedUpdate priority for the node.
dynamicNodeProviderNo(empty)A class name implementing MvcSiteMapProvider.Extensibility.IDynamicNodeProvider and providing dynamic nodes for the site map.

 

Defining sitemap nodes in code

In some cases, defining a sitemap node in code is more convenient than defining it in a sitemap xml file. To do this, decorate an action method with the MvcSiteMapNodeAttribute attribute. For example:

[code:c#]

// GET: /Checkout/Complete
[MvcSiteMapNodeAttribute(Title = "Checkout complete", ParentKey = "Checkout")]
public ActionResult Complete(int id)
{
    // ...
}

[/code]

Note that the ParentKey property should be specified to ensure the MvcSiteMapProvider  can determine the hierarchy for all nodes.

Dynamic sitemaps

In many web applications, sitemap nodes are directly related to content in a persistent store like a database.For example, in an e-commerce application, a list of product details pages in the sitemap maps directly to the list of products in the database. Using dynamic sitemaps, a small class can be provided to the MvcSiteMapProvider offering a list of dynamic nodes that should be incldued in the sitemap. This ensures the product pages do not have to be specified by hand in the sitemap XML.

First of all, a sitemap node should be defined in XML. This node will serve as a template and tell the MvcSiteMapProvider infrastructure to use a custom dynamic node procider:

[code:c#]

<mvcSiteMapNode title="Details" action="Details" dynamicNodeProvider="MvcMusicStore.Code.StoreDetailsDynamicNodeProvider, MvcMusicStore" />

[/code]

Next, a class implementing MvcSiteMapProvider.Extensibility.IDynamicNodeProvider or extending MvcSiteMapProvider.Extensibility.DynamicNodeProviderBase should be created in your application code. Here’s an example:

[code:c#]

public class StoreDetailsDynamicNodeProvider
    : DynamicNodeProviderBase
{
    MusicStoreEntities storeDB = new MusicStoreEntities();

    public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
    {
        // Build value
        var returnValue = new List<DynamicNode>();

        // Create a node for each album
        foreach (var album in storeDB.Albums.Include("Genre"))
        {
            DynamicNode node = new DynamicNode();
            node.Title = album.Title;
            node.ParentKey = "Genre_" + album.Genre.Name;
            node.RouteValues.Add("id", album.AlbumId);

            returnValue.Add(node);
        }

        // Return
        return returnValue;
    }
}

[/code]

Cache dependency

When providing dynamic sitemap nodes to the MvcSiteMapProvider, chances are that the hierarchy of nodes will become stale, for example when adding products in an e-commerce website. This can be solved by specifying a CacheDescriptor on your MvcSiteMapProvider.Extensibility.IDynamicNodeProvider implementation:

[code:c#]

public class StoreDetailsDynamicNodeProvider
    : DynamicNodeProviderBase
{
    MusicStoreEntities storeDB = new MusicStoreEntities();

    public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
    {
        // ...
    }

    public override CacheDescription GetCacheDescription()
    {
        return new CacheDescription("StoreDetailsDynamicNodeProvider")
        {
            SlidingExpiration = TimeSpan.FromMinutes(1)
        };
    }
}

[/code]

HtmlHelper functions

The MvcSiteMapProvider provides different HtmlHelper extension methods which you can use to generate SiteMap-specific HTML code on your ASP.NET MVC views. Here's a list of available HtmlHelper extension methods.

  • Html.MvcSiteMap().Menu() - Can be used to generate a menu
  • Html.MvcSiteMap().SubMenu() - Can be used to generate a submenu
  • Html.MvcSiteMap().SiteMap() - Can be used to generate a list of all pages in your sitemap
  • Html.MvcSiteMap().SiteMapPath() - Can be used to generate a so-called "breadcrumb trail"
  • Html.MvcSiteMap().SiteMapTitle() - Can be used to render the current SiteMap node's title

Note that these should be registered in Web.config, i.e. under <pages> add the following:

[code:c#]

<pages>
    <controls>
        <! -- ... -->
    </controls>
    <namespaces>
        <! -- ... -->
        <add namespace="MvcSiteMapProvider.Web.Html" />
    </namespaces>
</pages>

[/code]

Action Filter Attributes

SiteMapTitle

In some situations, you may want to dynamically change the SiteMap.CurrentNode.Title in an action method. This can be done manually by setting SiteMap.CurrentNode.Title, or by adding the SiteMapTitle action filter attribute.

Imagine you are building a blog and want to use the Blog's Headline property as the site map node title. You can use the following snippet:

[code:c#]

[SiteMapTitle("Headline")]
public ViewResult Show(int blogId) {
   var blog = _repository.Find(blogIdId);
   return blog;
}

[/code]

You can also use a non-strong typed ViewData value as the site map node title:

[code:c#]

[SiteMapTitle("SomeKey")]
public ViewResult Show(int blogId) {
   ViewData["SomeKey"] = "This will be the title";

   var blog = _repository.Find(blogIdId);
   return blog;
}

[/code]

Exporting the sitemap for search engine indexing

When building a website, chances are that you want to provide an XML sitemap used for search engine indexing. The XmlSiteMapResult class creates an XML sitemap that can be submitted to Google, Yahoo and other search engines to help them crawl your website better. The usage is very straightforward:

[code:c#]

public class HomeController
{
    public ActionResult SiteMapXml()
    {
        return new XmlSiteMapResult();
    }
}

[/code]

Optionally, a starting node can also be specified in the constructor of theXmlSiteMapResult .

Conclusion

Get it while it’s hot! MvcSiteMapProvider 2.0.0 is available on CodePlex.

kick it on DotNetKicks.com

This is an imported post. It was imported from my old blog using an automated tool and may contain formatting errors and/or broken images.

Leave a Comment

avatar

0 responses