Open roman-yagodin opened 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.
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.
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:
[x] Test if 8.0.4 website will work properly with newer DDRMenu assembly which contains some performance-related changes - not works just by replacing DLL, as it produces 500 error on admin/host pages - due to core DNN skin dependency? But works OK with R7.Epsilon.
[x] Limit menu depth considerably, use AJAX to get submenus - considering that submenu nodes have same or compatible view permissions. May not work if problem is in initial querying the nodes. - Limiting initial menu depth is not working, as it seems like all pages are getting for each menu instance nevertheless.
[x]
Search for other menu solutions.Probably there are no such complete and adopted to DNN specifics, as DDRMenu. Would need tons of code reimplementation and borrowing.[ ] Join primary/secondary menu rendering, then split resulting HTML markup into 2 parts with jQuery - ugly and unscalable hack.
[ ] Need to experiment with some kind of role checking node caching just after querying initial nodes (custom DLL). Also hacky way.
[ ] Does nodes population in DDR menu going at first request to some property (1), or during control initialization (2)? If (1), we could cache first bulk of menu nodes and reuse it between instances during same or subsequent web requests.
[ ] Explore using of NodeXmlPath - this way we can load external XML file with public menu structure. It can be generated on schedule, not as part of web request. But how can we attach non-public parts to it?
[ ] Limit non-public content (which need security checks) to distinct top-level menu nodes.
[ ] Replace IncludeNodes setting in config with the equivalent ExcludeNodes. Could give slight performance improvement.
[ ] Utilize SkinLocalization property - looks like it is internal and not accessible via SkinObject?[ ] Reduce number of DDR menu instances on the page #201
[x] Allow to cache DDR menu modules #178 - done!
[ ] Implement custom rendering cache provider, so we can rid of
Global.asax
and also resolve automatic cache variations then navigating between skin variants (e.g. from Home to general page).[ ] Extract DDR menu code into separate extension from Codeplex archive, port changes from DNN source, and try to resolve root issues. Codeplex archive: http://links.giveawayoftheday.com/dnnddrmenu.codeplex.com/. DNN source: https://github.com/dnnsoftware/Dnn.Platform/tree/develop/DNN Platform/Modules/DDRMenu. Codenames for project: DDRMenu3, DDR7Menu, R7.DDRMenu.
[ ] Need to check external NodeManipulators. They would require quite some time to get the data.