Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

SiteMapDataSource

Status
Not open for further replies.

robertfah

Programmer
Mar 20, 2006
380
US
I've got a SiteMapDataSource control that populates an asp:Menu control in my Master page. On issue I have with this is that everytime a new page is loaded, the menu is recreated, and in this case, the menu nodes and values are pulled from the Database. So my pages have slowed down considerably. Is there any way to cache the menu when the user first logs in and then retrieve it from cache on subsequent pages instead of loading it everytime?

HTML Code
Code:
<asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" ShowStartingNode="false" />
<asp:Menu ID="MainMenu" CssClass="menuspace" runat="server" Orientation="Horizontal"
    MaximumDynamicDisplayLevels="2" DataSourceID="SiteMapDataSource1" DynamicEnableDefaultPopOutImage="False"
    StaticEnableDefaultPopOutImage="False" DynamicPopOutImageUrl="~/images/arrow_rght.gif"
    OnMenuItemDataBound="MainMenu_MenuItemDataBound" ItemWrap="true">
    <StaticMenuStyle CssClass="menu" />
    <StaticMenuItemStyle CssClass="menuItem" />
    <StaticSelectedStyle CssClass="menuSelectedItem" />
    <DynamicMenuStyle CssClass="menuPopup" BackColor="#8C9FB4" />
    <DynamicMenuItemStyle CssClass="menuPopupItem" Font-Strikeout="False" />
    <DynamicHoverStyle CssClass="menuPopupItem" />
    <StaticHoverStyle CssClass="menuItemHover" />
</asp:Menu>

This is the class for the SqlSiteMapProvider
Code:
public class SqlSiteMapProvider : StaticSiteMapProvider 
{
	// Track the root node.
	private SiteMapNode rootNode;

	// Track the connection string, provider name, and stored procedure name.
    //private string connectionString;
    //private string providerName;
    //private string storedProcedure;

	private bool initialized = false;
	public virtual bool IsInitialized
	{
		get { return initialized; }
	}
    
	public override void Initialize(string name, System.Collections.Specialized.NameValueCollection attributes)
    {
        if (!IsInitialized)
        {
            base.Initialize(name, attributes);

            // Retrieve the web.config settings.
            //providerName = attributes["providerName"];
            //connectionString = attributes["connectionString"];
            //storedProcedure = attributes["storedProcedure"];

            //if (providerName == null || providerName.Length == 0)
            //    throw new Exception("The provider name was not found.");
            //else if (connectionString == null || connectionString.Length == 0)
            //    throw new Exception("The connection string was not found.");
            //else if (storedProcedure == null || storedProcedure.Length == 0)
            //    throw new Exception("The stored procedure name was not found.");

            initialized = true;
        }
    }

    public override SiteMapNode BuildSiteMap()
    {
        // Since the class is exposed to multiple pages,
        // use locking to make sure that the site map is not rebuilt by more than one
        // page at the same time.
        lock (this)
        {
            // Start with a clean slate.
            Clear();

            // Get the results in a DataSet
            DataSet ds = new DataSet();
            ds = UserInformation.Instance.GetMenuOptions();
            DataTable dtSiteMap = ds.Tables["MenuOptions"];

            // Get the root node.
            DataRow rowRoot = dtSiteMap.Select("ParentID IS NULL")[0];
            rootNode = new SiteMapNode(this,
                rowRoot["URL"].ToString(), rowRoot["URL"].ToString(),
                rowRoot["Title"].ToString(), rowRoot["Description"].ToString());
            string rootID = rowRoot["MenuOptionID"].ToString();

            // Fill down the hierarchy.
            AddChildren(rootNode, rootID, dtSiteMap);
        }

        return rootNode;
    }


	private void AddChildren(SiteMapNode rootNode, string rootID, DataTable dtSiteMap)//, System.Collections.IList iRoles)
	{
		DataRow[] childRows = dtSiteMap.Select("ParentID = " + rootID);
		foreach (DataRow row in childRows)
		{
            //SiteMapNode childNode = new SiteMapNode(this,
            //  row["URL"].ToString(), row["URL"].ToString(),
            //  row["Title"].ToString(), row["Description"].ToString());
            ////childNode.Roles = iRoles;
            //string rowID = row["MenuOptionID"].ToString();

            SiteMapNode childNode = new SiteMapNode(this,
                  row["MenuOptionID"].ToString(), row["URL"].ToString(),
                  row["Title"].ToString(), row["Description"].ToString());
            //childNode.Roles = iRoles;
            string rowID = row["MenuOptionID"].ToString();

			// Use the SiteMapNode AddNode method to add
			// the SiteMapNode to the ChildNodes collection.
			AddNode(childNode, rootNode);

            //Get around the glitch that the Base Class won't let you store a
            //URL inside the URL property.
            if (row["Title"].ToString() == "Calendar")
                childNode.Url = "[URL unfurl="true"]http://www.tssc.biz"[/URL] + row["URL"].ToString();

			// Check for children in this node.
			AddChildren(childNode, rowID, dtSiteMap);//, iRoles);
		}
	}

	protected override SiteMapNode GetRootNodeCore()
	{
        return BuildSiteMap();
	}

	public override SiteMapNode RootNode
	{
        get { return BuildSiteMap(); }
	}

	protected override void Clear()
	{
		lock (this)
		{
			rootNode = null;
			base.Clear();
		}
	}
}
 
Whenever you load your page, make sure you check for a Page.IsPostback and only bind your data if the Postback is False.

I don't call a specific function in the Page_Load event of the Master form that actually "loads" the SqlSiteMapProvider....it loads automatically. Are you suggesting that I move the DataSourceID="SiteMapDataSource1" out of the Menu in HTML and put it in the Page_Load only if it's a postback?
 
Yes, I would bind all of your data via code rather than rely on ASP.NET drag and drop controls. That way you can debug an control exactly what is happening much easier.


-------------------------------------------------------

Mark,
[URL unfurl="true"]http://aspnetlibrary.com[/url]
[URL unfurl="true"]http://mdssolutions.co.uk[/url] - Delivering professional ASP.NET solutions
[URL unfurl="true"]http://weblogs.asp.net/marksmith[/url]
 
And by writing your own code, you will be able to easily cache the data if necessary.
 
I just tried that and it doesn't work....main reason being that when I enter the main page from the login page, there's no postback happening, so it doesn't load the menu. It seems like I need to get the menu to load for the first time upon login, then the master page should somehow check to see if the menu has been rendered already...if so, make the DataSourceID="", else populate it which will populate the menu.

???
 
No, the master page should bind the data when the postback is False (i.e. the page has been loaded for the first time). For subsequent postbacks the data will be stored in ViewState so shouldn't be re-bound.


-------------------------------------------------------

Mark,
[URL unfurl="true"]http://aspnetlibrary.com[/url]
[URL unfurl="true"]http://mdssolutions.co.uk[/url] - Delivering professional ASP.NET solutions
[URL unfurl="true"]http://weblogs.asp.net/marksmith[/url]
 
No, the master page should bind the data when the postback is False (i.e. the page has been loaded for the first time). For subsequent postbacks the data will be stored in ViewState so shouldn't be re-bound.

For the same page, yes I can see that. But I'm talking about going from one page to another, the postback will always be false, therefore rendering the menu from the DB everytime.

Sorry if I confused you or mistyped it from above, but I'm looking to load the menu once (upon login), then on all other pages, just use a cached version, if possible.

I was thinking of this (not sure if it would work or if it's doable): user logs in, XML is then created for the menu, menu control is assigned to the xml file and gets read from the XML from that point on. yeah? neah?
 
But I'm talking about going from one page to another, the postback will always be false, therefore rendering the menu from the DB everytime.
That's where your caching would come in. Your function that returns the data for the datasource simply checks whether it is in cache or not and returns it if is is. If it isn't, then it retrives it and adds it to the cache before returning it.


-------------------------------------------------------

Mark,
[URL unfurl="true"]http://aspnetlibrary.com[/url]
[URL unfurl="true"]http://mdssolutions.co.uk[/url] - Delivering professional ASP.NET solutions
[URL unfurl="true"]http://weblogs.asp.net/marksmith[/url]
 
That's where your caching would come in.
Sorry for being ignorant on the subject, but how do I cache it and where can I tell if it's cached?
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top