r7-labs / R7.Epsilon

Highly customizable, multiportal, responsive skin for DNN Platform based on Bootstrap 4
GNU Affero General Public License v3.0
5 stars 2 forks source link

DDRMenu is slow #154

Open roman-yagodin opened 4 years ago

roman-yagodin commented 4 years ago

It takes around 3 sec. to render single menu on a ~3000 pages website (w/o caching). I believe it's mostly about getting and/or filtering the menu nodes, but first of all, we need to do some profiling to get real performance data.

Preferable way to address this issue is to find ways to optimize the DDRMenu, especially https://github.com/dnnsoftware/Dnn.Platform/blob/develop/DNN%20Platform/Modules/DDRMenu/MenuBase.cs

Other ideas:

roman-yagodin commented 4 years ago

Plans

roman-yagodin commented 4 years ago

Experimenting with caching:

using System.Collections.Generic;
using R7.Epsilon.Menus;
using DotNetNuke.Web.DDRMenu.TemplateEngine;
using DDRMenu = DotNetNuke.Web.DDRMenu;
using DotNetNuke.Common.Utilities;
using System.Web.UI;
using System.IO;
using System.Text;

namespace R7.Epsilon.Skins.SkinObjects
{
    public class PrimaryMenu: EpsilonMenuBase
    {
        DDRMenu.SkinObject LoadMenu ()
        {
            //var menu = (DDRMenu.SkinObject) LoadControl ("~/DesktopModules/DDRMenu/Menu.ascx");
            var menu = new DDRMenu.SkinObject ();

            menu.ID = "primaryMenu";
            menu.ClientIDMode = ClientIDMode.Static;
            menu.MenuStyle = "MegaDrop";
            menu.NodeSelector = Config.PrimaryMenu.NodeSelector;
            menu.IncludeNodes = Config.PrimaryMenu.IncludeNodes;
            menu.ExcludeNodes = Config.PrimaryMenu.ExcludeNodes ?? Config.Menu.ExcludeNodes;

            if (menu.TemplateArguments == null) {
                menu.TemplateArguments = new List<TemplateArgument> ();
            }

            menu.TemplateArguments.Add (new TemplateArgument ("UrlFormat", Config.PrimaryMenu.UrlFormat ?? Config.Menu.UrlFormat));

            menu.NodeManipulator = typeof (EpsilonNodeManipulator<PrimaryMenu>).FullName;

            return menu;
        }

        protected override void Render (HtmlTextWriter writer)
        {
            var html = DataCache.GetCache<string> ("__menukey123");
            if (html == null) {
                var menu = LoadMenu ();
                var builder = new StringBuilder ();
                var htmlWriter = new HtmlTextWriter (new StringWriter (builder));

                menu.RenderControl (htmlWriter);
                //htmlWriter.Flush ();
                //htmlWriter.Close ();

                html = "Cached menu: " + builder;
                DataCache.SetCache ("__menukey123", html);
            }

            writer.Write (html);
        }
    }
}

This code doesn't work - string builder is empty, a null reference exception produced in the DDRMenu.SkinObject.

Copying DDRMenu/SkinObject.cs in order to produce custom implementation over MenuBase also not works, as MenuBase.RootNode, MenuBase.ApplySettings, etc. are marked internal. Another reason for forking DDRMenu?..

Need debug symbols to locate null reference source it order to fill an issue.

roman-yagodin commented 4 years ago
at DotNetNuke.Web.DDRMenu.SkinObject.Render(HtmlTextWriter writer)
in C:\TeamCity\buildAgent\work\DNN_Platform_804_Public\Packaging\
DNN.Platform\DNN Platform\Modules\DDRMenu\SkinObject.cs:line 88

It's here:

protected override void Render(HtmlTextWriter writer)
{
    using (new DNNContext(this))
    {
        try
        {
            base.Render(writer);
            menu.Render(writer); // <== !!!
        }
        catch (Exception exc)
        {
            Exceptions.ProcessModuleLoadException(this, exc);
        }
    }
}

Still not very helpful.