Performance of Navigation Module

Topics: General, Troubleshooting
Jun 3, 2013 at 12:05 PM
I have navigation menu with 150 Content Menu Items.
When widget with this menu added on simple page that page load 2.6 sec.
Simple page without widget with this menu load 0.6 sec.

What can i do that my site with menu work faster ?
Developer
Jun 3, 2013 at 1:12 PM
Enable output caching and/or use Custom Link instead of Content Menu Item.
Jun 3, 2013 at 7:29 PM
Edited Jun 3, 2013 at 7:54 PM
After use Custom Link my simple page render 0.92 sec (simple page with one menu). Thanks.

But my other page needs 2 menu: breadcrumbs and left menu (for main menu widget i use other menu with 6 custom links).
BuildMenu funtion takes 50% load page (i check it by DotTrace). I think it very bad factor for orchard cms.

Now main menu and other menu (breadcrumbs, left menu) not connected and does not work right. How i can fix it?

Do you mean enable IIS output caching or orchard module?
Developer
Jun 10, 2013 at 12:01 AM
If I recall correctly, there was a fix for this in the 1.x branch, but I'm not sure. You could try it.

With output caching I meant the Orchard module (I think it's Contrib.Cache).
Jun 18, 2013 at 6:48 PM
Hi neTp9c,

I have had similar issues with large navigation menus. The Contrib.Cache module is great, however, there still seems to be a need for a dedicated navigation cache since the contrib.cache works on the output (which means the first time every page loads it will be slow). I have made a module which implements cache for the navigation system itself (so it caches the FULL navigation system on the first page load and every subsequent load is then MUCH faster). I have shaved a couple seconds off of my website load time. You can look at (and feel free to try out) the module here: https://navigationcache.codeplex.com/ It also works with logged in users as the cached menu is specific to the user (I don't think the Contrib.Cache module does for security reasons).

One thing I will add though, even with this navigation caching, the first page load is still tied to the performance of the page so it is still best to limit the scope of the navigation system. I LOVE breadcrumbs but I have found it is often better to emulate a breadcrumb for a content type rather than actually adding every content item into the Menu hierarchy. For example, say you want Blog Posts to have the breadcrumb. This could cause your menus to be large really fast so the best thing to do is have the Blog Posts inject a part into the zone where your breadcrumb normally is located then style the html to look exactly like your breadcrumb. This approach has shaved a couple seconds off of one of my larger websites.

Hope this information is useful and let me know if you have any questions / improvements. I am actively using it on 4 websites and am not having trouble.
Jun 26, 2013 at 12:03 AM
Edited Jun 26, 2013 at 12:13 AM
Thanks jao28. Your module is good.
My be you have any other useful modules? :)

Improvements:

I think it would be better to check menu items by url (if using right seo urls). There will be no need to add Blog Posts to menu. I do that for my website.
For that you can use other function insted of NavigationHelper.SetSelectedPath
using Orchard.UI.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;

namespace Opt.Navigation.Helpers
{
    public class NavigationHelper
    {
        public static Stack<MenuItem> SetSelectedPath(IEnumerable<MenuItem> menuItems, RouteValueDictionary currentRouteData, string url)
        {
            if (menuItems == null)
                return null;

            foreach (MenuItem menuItem in menuItems)
            {
                Stack<MenuItem> selectedPath = SetSelectedPath(menuItem.Items, currentRouteData, url);
                if (selectedPath != null)
                {
                    menuItem.Selected = true;
                    selectedPath.Push(menuItem);
                    return selectedPath;
                }

                if (RouteMatches(menuItem.RouteValues, currentRouteData) || UrlMatches(menuItem.Href, url))
                {
                    menuItem.Selected = true;

                    selectedPath = new Stack<MenuItem>();
                    selectedPath.Push(menuItem);
                    return selectedPath;
                }
            }

            return null;
        }

        public static bool RouteMatches(RouteValueDictionary itemValues, RouteValueDictionary requestValues)
        {
            if (itemValues == null && requestValues == null)
            {
                return true;
            }
            if (itemValues == null || requestValues == null)
            {
                return false;
            }
            if (itemValues.Keys.Any(key => requestValues.ContainsKey(key) == false))
            {
                return false;
            }
            return itemValues.Keys.All(key => string.Equals(Convert.ToString(itemValues[key]), Convert.ToString(requestValues[key]), StringComparison.OrdinalIgnoreCase));
        }

        public static bool UrlMatches(string itemUrl, string requestUrl)
        {
            //if (itemUrl == requestUrl || requestUrl.StartsWith(itemUrl + "/"))
            if(requestUrl.StartsWith(itemUrl))
            {
                return true;
            }

            return false;
        }
    }
}
Jun 26, 2013 at 10:37 AM
Hi neTp9c,

Thanks for the suggestion, I will take a look and see if I can take the improvements you mention above into the module. Will need to see how the Url Matching works and if it works if the page is not a standard content item, yes, that does still happen :).

I do have a couple other modules out there. One recent release you may be interested is the Social Metadata module: https://socialmetadata.codeplex.com/. It allows you to easily (with Tokens) control the metadata being put out for the different social networks.

There is also a PayPal module out their for the Nwazet.Commerce module which I am using (and others are also) though I am going to further isolate it since right now it uses a special variant of the Nwazet.Commerce module to work. You can watch it / snag it here: https://bitbucket.org/ems/nwazet.commerce

So, if you have needs, just throw them out :) and I can likely tell you what is built or, if I can grab the time, even build a module.