Click here to Skip to main content
15,867,330 members
Articles / Web Development / ASP.NET

MVC SQL Site Map Provider

Rate me:
Please Sign up or sign in to vote.
3.50/5 (2 votes)
12 Jan 2009CPOL1 min read 44.9K   850   21   6
An article on implementing a SQL site map provider for MVC.

Introduction

I’m developing my first MVC application. I’ve been working with ASP.NET for a couple of years, and only now have I got some contact with MVC.

First of all, I’ve defined the application layout and I realized that I needed to display the menu and the site map path dynamically. After some research, I couldn’t find any SQL site map provider for MVC. The only sample that I found was the MVCSiteMapProvider posted by Maarten Balliauw, and it has XML as the data source. With this sample, I’ve developed the MVCSQLSiteMapProvider. This is only a prototype, and it could be improved. I’ve only implemented the base functionalities so far. So, if you have any suggestions, just write them down.

Using the code

The site map data model is similar to the XML site map provider. So, it’s possible to define a node with the corresponding URL or with the controller/action.

Database.png

The site map provider class inherits from the StaticSiteMapProvider and, as I said before, I’ve only implemented what I needed so far.

C#
public class MvcSQLSitemapProvider : StaticSiteMapProvider
{
    private const string _errormsg1 = "Missing node ID";
    private const string _errormsg2 = "MDuplicate node ID";
    private const string _errormsg3 = "Missing parent ID";
    private const string _errormsg4 = "Invalid parent ID";
    private const string _errormsg5 = "Empty or missing connection string";
    private const string _errormsg6 = "Missing connection string";
    private const string _errormsg7 = "Empty connection string";
    private string _connect = String.Empty;
    private Dictionary _nodes = new Dictionary(16);
    private SiteMapNode _root;
    public override void Initialize(string name, NameValueCollection attributes)
    {
        if (attributes == null)
            throw new ArgumentNullException("attributes");
        if (string.IsNullOrEmpty(name))
            name = "MvcSitemapProvider";
        if (string.IsNullOrEmpty(attributes["description"]))
        {
            attributes.Remove("description");
            attributes.Add("description", "MVC site map provider");
        }
        base.Initialize(name, attributes);
        string connect = attributes["connectionStringName"];
        if (string.IsNullOrEmpty(connect))
            throw new ProviderException(_errormsg5);
        attributes.Remove("connectionStringName");
        if (WebConfigurationManager.ConnectionStrings[connect] == null)
            throw new ProviderException(_errormsg6);
        _connect = WebConfigurationManager.ConnectionStrings[connect].ConnectionString;
        if (string.IsNullOrEmpty(_connect))
            throw new ProviderException(_errormsg7);
        if (attributes.Count > 0)
        {
            string attr = attributes.GetKey(0);
            if (!string.IsNullOrEmpty(attr))
                throw new ProviderException(string.Format(
                  "Unrecognized attribute: {0}", attr));
        }
    }
    public override SiteMapNode BuildSiteMap()
    {
        lock (this)
        {
            if (_root != null)
                return _root;
            SiteMapContextDataContext db = new SiteMapContextDataContext(_connect);
            var siteMpaQuery = from s in db.SITEMAPs
                               orderby s.ID
                               select s;
            foreach (var item in siteMpaQuery)
            {
                if (item.Equals(siteMpaQuery.First()))
                {
                    _root = CreateSiteMapFromRow(item);
                    AddNode(_root, null);
                }
                else
                {
                    SiteMapNode node = CreateSiteMapFromRow(item);
                    AddNode(node, GetParentNodeFromNode(item));
                }
            }
            return _root;
        }
    }
    private SiteMapNode CreateSiteMapFromRow(SITEMAP item)
    {
        if (_nodes.ContainsKey(item.ID))
            throw new ProviderException(_errormsg2);
        SiteMapNode node = new SiteMapNode(this, item.ID);
        if (!string.IsNullOrEmpty(item.URL))
        {
            node.Title = string.IsNullOrEmpty(item.TITLE) ? null : item.TITLE;
            node.Description = 
             string.IsNullOrEmpty(item.DESCRIPTION) ? null : item.DESCRIPTION;
            node.Url = string.IsNullOrEmpty(item.URL) ? null : item.URL;
        }
        else
        {
            node.Title = string.IsNullOrEmpty(item.TITLE) ? null : item.TITLE;
            node.Description = 
             string.IsNullOrEmpty(item.DESCRIPTION) ? null : item.DESCRIPTION;
            IDictionary routeValues = new Dictionary();
            if (string.IsNullOrEmpty(item.CONTROLLER))
                routeValues.Add("controller", "Home");
            else
                routeValues.Add("controller", item.CONTROLLER);
            if (string.IsNullOrEmpty(item.CONTROLLER))
                routeValues.Add("action", "Index");
            else
                routeValues.Add("action", item.ACTION);
            if (!string.IsNullOrEmpty(item.PARAMID))
                routeValues.Add("id", item.PARAMID);
            HttpContextWrapper httpContext = new HttpContextWrapper(HttpContext.Current);
            RouteData routeData = RouteTable.Routes.GetRouteData(httpContext);
            if (routeData != null)
            {
                VirtualPathData virtualPath = routeData.Route.GetVirtualPath(
                  new RequestContext(httpContext, routeData), 
                  new RouteValueDictionary(routeValues));
                if (virtualPath != null)
                {
                    node.Url = "~/" + virtualPath.VirtualPath;
                }
            }
        }
        _nodes.Add(item.ID, node);
        return node;
    }
    private SiteMapNode GetParentNodeFromNode(SITEMAP item)
    {
        if (!_nodes.ContainsKey(item.PARENT_ID))
            throw new ProviderException(_errormsg4);
        return _nodes[item.PARENT_ID];
    }
    protected override SiteMapNode GetRootNodeCore()
    {
        return BuildSiteMap();
    }
}

I’ve also created three user controls. The first one is used to display the main menu. It retrieves all the root child nodes and displays them.

Menu.png

The second one retrieves the site map path to the current page.

SiteMapPath.png

The last control is used to display the content menu, which retrieves all the current child nodes.

ContentMenu.png

These controls are very simple and I didn’t take much time on making them good looking. Smile

Well, I hope this post was useful.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Portugal Portugal
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 2 Pin
chirag700413-Jul-12 21:16
chirag700413-Jul-12 21:16 
QuestionDB Schema Pin
gntombel13-Feb-09 0:32
gntombel13-Feb-09 0:32 
AnswerRe: DB Schema Pin
HuntTheShunt21-Jan-10 4:26
HuntTheShunt21-Jan-10 4:26 
GeneralI don't know what is MVC. Pin
fam6n14-Jan-09 5:24
fam6n14-Jan-09 5:24 
GeneralRe: I don't know what is MVC. Pin
TODODotNet14-Jan-09 7:30
TODODotNet14-Jan-09 7:30 
Of course... I forgot to mention that it refers to ASP.NET MVC[^]

TODO

GeneralNice work Pin
darrenmiller12-Jan-09 14:50
darrenmiller12-Jan-09 14:50 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.