Skip to content

Upgrading from v3 to v4

Shad Storhaug edited this page Apr 24, 2018 · 3 revisions

MvcSiteMapProvider has had some significant updates going from v3 to v4. This page outlines the upgrade path from v3 to v4.

WARNING: MvcSiteMapProvider requires NuGet 2.6 or higher to ensure your configuration is not overwritten when upgrading. Before you upgrade MvcSiteMapProvider, you should install the latest version of NuGet.

Updating the NuGet package

The first part of the upgrade from v3 to v4 will be updating the NuGet package. Before, MvcSiteMapProvider only had one NuGet package. Today, it has been split in multiple, of which the following ones are good to know at this time:

  • MvcSiteMapProvider.Web containing all views and web.config changes
  • MvcSiteMapProvider.MVC<version>.Core containing the library itself

Upgrading from v3 to v4 consists of installing the correct packages for your ASP.NET MVC version:

  • For MVC 2, uninstall MvcSiteMapProvider and install MvcSiteMapProvider.MVC2
  • For MVC 3, uninstall MvcSiteMapProvider and install MvcSiteMapProvider.MVC3
  • For MVC 4, uninstall MvcSiteMapProvider and install MvcSiteMapProvider.MVC4
  • For MVC 5, uninstall MvcSiteMapProvider and install MvcSiteMapProvider.MVC5
  • Note that for MVC 4 we have made it possible to upgrade MvcSiteMapProvider instead, which will pull in all required dependencies. Do know that this is not the recommended scenario and it is preferred to install MvcSiteMapProvider.MVC4 instead.

The MvcSiteMapProvider.Web update will add views and all required runtime dependencies to your project. This package is a dependency of each of the above options and generally will not need to be installed explicitly.

Note: The MvcSiteMapProvider.Web package contains some logic to attempt to detect the type of template files to install. If you have both .aspx and .cshtml extensions in your project, it will default to .aspx templates. This may not always be the right decision. If you find that you have the wrong templates installed in your Views/Shared/DisplayTemplates folder, you can safely delete them and install the razor templates from the DisplayTemplates folder in MvcSiteMapProvider.

In .NET versions prior to .NET 4.0, one line of code should be added to the Application_Start() event of Global.asax:

MvcSiteMapProvider.DI.Composer.Compose();

Note that this code is automatically executed if using .NET 4.0 or higher by the use of WebActivator, so in most cases you will not need to call it manually.

Updating configuration

MvcSiteMapProvider v3 was based on the ASP.NET provider model. In v4, we have removed that dependency. This brings some changes to configuration.

Depending on how you load the new version of MvcSiteMapProvider the configuration may be different. However since this is an upgrade the default internal DI container will be used. Refer to the configuration reference page for detailed information about configuring MvcSiteMapProvider using an external DI container.

In short, the following configuration from v3 should be removed from your Web.config file:

<siteMap>
  <providers>
    <add name="MvcSiteMapProvider" type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider" 
         siteMapFile="~/Mvc.Sitemap"
         securityTrimmingEnabled="true"
         cacheDuration="5"
         enableLocalization="true"
         scanAssembliesForSiteMapNodes="true" 
         excludeAssembliesForScan="" 
         includeAssembliesForScan="" 
         attributesToIgnore="bling,visibility" 
         nodeKeyGenerator="MvcSiteMapProvider.DefaultNodeKeyGenerator, MvcSiteMapProvider" 
         controllerTypeResolver="MvcSiteMapProvider.DefaultControllerTypeResolver, MvcSiteMapProvider"
         actionMethodParameterResolver="MvcSiteMapProvider.DefaultActionMethodParameterResolver, MvcSiteMapProvider" 
         aclModule="MvcSiteMapProvider.DefaultAclModule, MvcSiteMapProvider"
         routeMethod=""
         siteMapNodeUrlResolver="MvcSiteMapProvider.DefaultSiteMapNodeUrlResolver, MvcSiteMapProvider" 
         siteMapNodeVisibilityProvider="MvcSiteMapProvider.DefaultSiteMapNodeVisibilityProvider, MvcSiteMapProvider" 
         siteMapProviderEventHandler="MvcSiteMapProvider.DefaultSiteMapProviderEventHandler, MvcSiteMapProvider" />
  </providers>
</siteMap>

The above configuration will not be picked up by MvcSiteMapProvider v4. Instead, the following configuration settings can be provided in the web.config file (all settings are optional and default to what is shown here if not provided):

<appSettings>
    <add key="MvcSiteMapProvider_UseExternalDIContainer" value="false"/>
    <add key="MvcSiteMapProvider_SiteMapFileName" value="~/Mvc.sitemap"/>
    <add key="MvcSiteMapProvider_ScanAssembliesForSiteMapNodes" value="false"/>
    <add key="MvcSiteMapProvider_ExcludeAssembliesForScan" value=""/>
    <add key="MvcSiteMapProvider_IncludeAssembliesForScan" value=""/>
    <add key="MvcSiteMapProvider_AttributesToIgnore" value=""/>
    <add key="MvcSiteMapProvider_CacheDuration" value="5"/>
    <add key="MvcSiteMapProvider_ControllerTypeResolverAreaNamespacesToIgnore" value=""/>
    <add key="MvcSiteMapProvider_DefaultSiteMapNodeVisibiltyProvider" value=""/>
    <add key="MvcSiteMapProvider_VisibilityAffectsDescendants" value="true"/>
    <add key="MvcSiteMapProvider_SecurityTrimmingEnabled" value="false"/>
    <add key="MvcSiteMapProvider_EnableLocalization" value="true"/>
    <add key="MvcSiteMapProvider_EnableSitemapsXml" value="true"/>
    <add key="MvcSiteMapProvider_EnableResolvedUrlCaching" value="true"/>
    <add key="MvcSiteMapProvider_EnableSiteMapFile" value="true"/>
    <add key="MvcSiteMapProvider_IncludeRootNodeFromSiteMapFile" value="true"/>
    <add key="MvcSiteMapProvider_EnableSiteMapFileNestedDynamicNodeRecursion" value="false"/>
    <add key="MvcSiteMapProvider_UseTitleIfDescriptionNotProvided" value="true"/>
</appSettings>

Many of these settings map to their v3 counterpart and their values can be copied in. All type overrides that were available, for example for specifying which aclModule to use, have been moved towards a dependency injection strategy. If you need to override any of these types (or others, more overrides are available in v4), refer to the dependency injection documentation.

Updating sitemap XML

As with any major version upgrade we have had earlier, the XML schema for your sitemap file should updated. For this upgrade it should be changed from http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-3.0 to http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0.

You must also remove the enableLocalization attribute from the mvcSiteMap element (if it exists) and migrate the value to the web.config appSettings configuration value MvcSiteMapProvider_EnableLocalization as shown above.

Updating System.Web.SiteMap Code References

If you have references in your code to Microsoft's SiteMap static methods, you will need to update them to the MvcSiteMapProvider equivalent.

In general, any reference to System.Web.SiteMap.Provider will need to be updated to MvcSiteMapProvider.SiteMaps.Current.

If you are using named providers and therefore have more than one sitemap in your project, you will need to use an external DI container and follow the directions to use more than one sitemap in a single application.

Updating Sitemaps End Point (/sitemap.xml) Code

If you have placed the line to register the /sitemap.xml endpoint in your Global.asax or other part of your initialization, you should remove it as it is now enabled by default.

// Remove this line
XmlSiteMapController.RegisterRoutes(RouteTable.Routes);

Note: If you installed one of the DI packages, this line will be put into the file /App_Start/MvcSiteMapProviderConfig.cs. You should not remove the line from that file.

If you do not want the /sitemap.xml endpoint registered in your application, it can be disabled in the web.config appSettings section:

<add key="MvcSiteMapProvider_EnableSitemapsXml" value="false"/>

Updating SiteMapPreserveRouteDataAttribute

SiteMapPreserveRouteDataAttribute has been deprecated because it relied on a bug (that has been fixed) in order to function. It is planned for removal from the project in version 5.

The recommended migration path is to start using the PreservedRouteParameters property instead. You must specify each parameter that you want preserved on each node you want it preserved on. This is only useful in cases where you want to use the SiteMapPath HTML helper to work alongside navigation that is driven from a database and there is no value in adding them as nodes.

However, if there is value in adding the records as nodes (such as if you want them to be indexed by search engines), you should use a Dynamic Node Provider to set the value instead as shown in Routing Basics.

Updating Dynamic Node Providers, Visibility Providers, and URL Resolvers

While in v3 only the dynamic node provider had a base class, now each of these providers has a base class. Technically the interface is all that is needed, but the base classes provide a shortcut so there is less for you to implement. It is recommended to always use the base class except for advanced scenarios.

The following table shows the typical upgrade paths from v3 to v4:

Provider Version 3 Use Version 4 Use
Dynamic Node Provider MvcSiteMapProvider.Extensibility.DynamicNodeProviderBase MvcSiteMapProvider.DynamicNodeProviderBase
Visibility Provider MvcSiteMapProvider.Extensibility.ISiteMapNodeVisibilityProvider MvcSiteMapProvider.SiteMapNodeVisibilityProviderBase
URL Resolver MvcSiteMapNode.Extensibility.ISiteMapNodeUrlResolver MvcSiteMapProvider.Web.UrlResolver.SiteMapNodeUrlResolverBase

Dynamic Node Provider

In addition to the namespace change of the base class, the interface has also changed.

The GetDynamicNodeCollection() method now has a parameter that the template node is passed through, so you can easily read and use the values from this node if needed:

// v3
IEnumerable<DynamicNode> GetDynamicNodeCollection();

// v4
IEnumerable<DynamicNode> GetDynamicNodeCollection(ISiteMapNode node);

The GetCacheDescription() method no longer exists because there is now a single cache that controls the timeout for the entire sitemap, so any implementation should be removed.

If you need to update the sitemap with dynamic data, it is recommended to call the SiteMaps.ReleaseSiteMap() method immediately after the data has been updated in the database. This will force the sitemap to be rebuilt and cached with the new data on the next user request.

Visibility Provider

In addition to the new base class, the interface has also changed.

The IsVisible() method no longer has a context parameter, and the type being passed is now MvcSiteMapProvider.ISiteMapNode rather than System.Web.SiteMapNode:

// v3
bool IsVisible(SiteMapNode node, HttpContext context, IDictionary<string, object> sourceMetadata);

// v4
bool IsVisible(ISiteMapNode node, IDictionary<string, object> sourceMetadata);

URL Resolver

In addition to the new base class, the interface has also changed.

The ResolveUrl() method node parameter type is now MvcSiteMapProvider.ISiteMapNode rather than MvcSiteMapProvider.MvcSiteMapNode and its name changed to node:

// v3
string ResolveUrl(MvcSiteMapNode mvcSiteMapNode, string area, string controller, string action, IDictionary<string, object> routeValues);

// v4
string ResolveUrl(ISiteMapNode node, string area, string controller, string action, IDictionary<string, object> routeValues);

A Note on the AppliesTo() Method

This method exists on each of the above interfaces and each of the base classes provides a default implementation that will mimic the v3 behavior. In general, you will not need to deal with it unless you are using strongly named assemblies or implementing the interfaces directly.

v3 used .NET Reflection to load the providers into memory on demand, and in each node had a new instance created, used, and then garbage collected. In v4, we take a different approach - there is now a single instance of each provider that is kept in memory regardless of how many nodes are instantiated that services all of the nodes that call it by its name.

A string match on the name is used to determine which provider instance should be used to service a given node using the Strategy Pattern. The AppliesTo() method contains the logic to match the requested provider, and returns true if the provider is a match. To understand its usage, see this Stack Overflow question: http://stackoverflow.com/questions/1499442/best-way-to-use-structuremap-to-implement-strategy-pattern.

For backward compatibility with v3, we have provided the base classes mentioned above that default to matching the short assembly qualified name to the value in the .sitemap file, which is the same string that is used by .NET Reflection to load the type.


Want to contribute? See our Contributing to MvcSiteMapProvider guide.



Version 3.x Documentation


Unofficial Documentation and Resources

Other places around the web have some documentation that is helpful for getting started and finding answers that are not found here.

Tutorials and Demos

Version 4.x
Version 3.x

Forums and Q & A Sites

Other Blog Posts

Clone this wiki locally